// PropertyResourceSaver.java
// $Id: PropertyResourceSaver.java,v 1.8 1997/07/30 14:08:31 ylafon Exp $  
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.tools.resources.property;

import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;

import w3c.util.*;
import w3c.tools.resources.converter.*;
import w3c.tools.resources.*;
import w3c.tools.resources.event.*;
import w3c.tools.resources.impl.*;
import w3c.tools.resources.PersistentReference;

public class PropertyResourceSaver implements ResourceSaver {

  /**
   * Properties - Name of the default resource class.
   */
  public static final
  String DEFAULTCLASS_P = "w3c.tools.resources.property.defaultClass";

  /**
   * Our ResourceSpace.
   */
  protected ResourceSpaceImpl space = null;
  
  /**
   * Name of the default resource class. 
   */
  protected String defaultClass = null;

  public synchronized void save (Resource r) {
    ResourceImpl pr = (ResourceImpl) r;
    ResourceReference rr = null;
    try {
      rr = pr.getContainer();
      // rr == null if it's the root resource.
      if (rr != null) {
	ResourceContainerImpl c = (ResourceContainerImpl)rr.lock();
	save(pr,c);
      }
    } finally {
      if (rr != null) rr.unlock();
    }
  }


  /**
   * Save the given resource in a properties file located in the
   * container directory.
   * @param r The PropertyResource to save
   * @param c The resource container
   */
  // use a ResourceReference instead ??
  public synchronized void save(ResourceImpl r, 
				ResourceContainerImpl c) 
    throws RuntimeException
  {
    File out = c.getStore().getResourceFile(r.getName());
    Properties P = new Properties();
    // saving the property Resource itself.
    saveResourceInProperties(r,P,false);
    // saving its delegatee
    ResourceReference delegatees [] = r.getDelegatees();
    if (delegatees.length > 0) {
      StringBuffer buffer = new StringBuffer();
      for (int i=0; i < delegatees.length; i++){
	try {
	  Resource deleg = delegatees[i].lock();
	  buffer.append(deleg.getName());
	} finally {
	  delegatees[i].unlock();
	}
	if (i < delegatees.length - 1) buffer.append("|");
      }

      P.put("delegatees", new String(buffer));

      for (int i=0; i < delegatees.length; i++){
	try {
	  Resource deleg = delegatees[i].lock();
	  saveResourceInProperties(deleg,P,true);
	} finally {
	  delegatees[i].unlock();
	}
      }
    }
    try {
      BufferedOutputStream buffer = 
	new BufferedOutputStream ( new FileOutputStream(out));
      P.save( buffer, "Property resource");
      buffer.close();
    } catch (IOException ex) {
      throw new RuntimeException("Unable to save Properties file : "+
				 out.getAbsolutePath());
    }
  }



  protected synchronized void saveResourceInProperties(Resource r, 
						       Properties P,
						       boolean delegatee) 
  {
    String name = null;
    String pname = null;

    name = ((delegatee)
	    ? r.getName()+"."
	    : "");
    Enumeration e = 
      (PropertyHolderInspector.getProperties(r.getClass())).keys();

    P.put(name+"class",r.getClass().getName());
    Object value = null;
    while (e.hasMoreElements()) {
      try {
	pname = (String)e.nextElement();
	value = r.getValue(pname);
	if (value != null) {
	  P.put(name+pname,ConvertRegistry.toString(value));
	  P.put(name+pname+".type",value.getClass().getName()); 
	}
      } catch (IllegalAccessException ex) {
	System.out.println(ex.getMessage());
	ex.printStackTrace();
      } catch (NoSuchConverterException ex) {
	System.out.println(ex.getMessage());
	ex.printStackTrace();
      }
    }
  }  


  public synchronized ResourceReference load (PersistentReference pr,
					      ResourceReference container) 
    throws ClassNotFoundException
  {
    PersistentReferenceImpl prImpl = (PersistentReferenceImpl) pr;
    File file = new File(prImpl.containerPath,prImpl.resourceName+".p");
    if (! file.exists()) return null;
    return load(file,prImpl.resourceName,container);
  }
					      

  /**
   * Load the resource store in the given File.
   * @param file The file containing the persistent version of the resource
   * @param name The resource name
   * @param container The Container of the resource
   */
  protected synchronized ResourceReference load(File file, 
						String name, 
						ResourceReference container) 
    throws ClassNotFoundException
  {
    ResourceReference rr = null;
    Resource r = null;
    try {
      ResourceContext context = null;
      try {
	ResourceContainerImpl cont = (ResourceContainerImpl)container.lock();
	context = (cont.getContext()).getClone();
	context.setContainer(container);
      } catch (Exception e) {
	System.out.println(e.getMessage());
	e.printStackTrace();
      } finally {
	container.unlock();
      }
	// new ResourceContextImpl(space,container);
      Properties P = new Properties();
      BufferedInputStream buff = null;
      try {
	buff = new BufferedInputStream( new FileInputStream(file));
	P.load(buff);
	buff.close();
      } catch (IOException e) {
	System.out.println(e.getMessage());
	e.printStackTrace();
	try { if (buff != null) buff.close(); } catch (Exception ex) {}
	return null;
      }
      // loading the Resource itself 
      rr = loadResource(P,container,context,name,false);
      // loading its local Delegatees
      String delegateesNames = P.getProperty("delegatees");
      if (delegateesNames != null) {
	StringTokenizer tok = new StringTokenizer(delegateesNames,"|");
	try {
	  r = rr.lock();	  
	  while (tok.hasMoreTokens()) {
	    String delegateeName = tok.nextToken();
	    ResourceContext ctxt = null;
	    try {
	      ResourceContainerImpl cont = 
		(ResourceContainerImpl)container.lock();
	      ctxt = (cont.getContext()).getClone();
	      ctxt.setContainer(container);
	    } finally {
	      container.unlock();
	    }
	    //ResourceContext ctxt = new ResourceContextImpl(space,container);
	    r.addDelegatee(loadResource(P,
					container,
					ctxt,
					delegateeName,
					true));
	  }
	} finally {
	  rr.unlock();
	}
      }
      return rr;
    } catch (Exception ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
      return rr;
    }
  }

  protected ResourceReference loadResource (Properties P,
					    ResourceReference container,
					    ResourceContext context,
					    String name,
					    boolean delegatee) 
    throws ClassNotFoundException, 
           InstantiationException, 
	   IllegalAccessException
  {
    PersistentReference pr = null;
    ResourceReference rr   = null;
    Resource r             = null;
    String classname       = ((delegatee)
			      ? P.getProperty(name+".class")
			      : P.getProperty("class"));

    try {
      ResourceContainerImpl pCont = 
	(ResourceContainerImpl)container.lock();
      pr = pCont.getStore().getPersistentReference(name);
    } finally {
      container.unlock();
    }

    if (classname == null) classname = defaultClass;
    Class rclass = null;
    rclass = Class.forName(classname);
    r = (Resource)rclass.newInstance();

    Enumeration e = (PropertyHolderInspector.getProperties(rclass)).keys();
    String field = null; String value = null; String type  = null; 
    String attrname = null;
    while (e.hasMoreElements()) {
      attrname = (String)e.nextElement();
      field = ((delegatee)
	       ? name+"."+attrname
	       : attrname);
      value = P.getProperty(field);
      type = P.getProperty(field+".type");
      Object obj = null;
      if (value != null) {
	try {
	  obj = ConvertRegistry.toObject(Class.forName(type),value);
	  context.addProperty(attrname,obj);
	} catch (NoSuchConverterException ex) {
	  System.out.println(ex.getMessage());
	  ex.printStackTrace();
	}
      }
    }
    rr = space.getCache().getReference(pr,r);
    context.setResourceReference(rr);
    try {
      r.create(context);
      r.init(context);
    } catch (Exception ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
    }finally {
      return rr;
    }
  }

  public synchronized void delete (Resource r) {
    ResourceReference rcont = ((ResourceImpl)r).getContainer();
    try {
      ResourceContainerImpl cont = (ResourceContainerImpl)rcont.lock();
      File prop = cont.getStore().getResourceFile(r.getName());
      if (prop.exists()) prop.delete();
    } finally {
      rcont.unlock();
    }
  }

  public synchronized void delete (String name, 
				   ResourceReference container) 
  {
    ResourceContainerImpl cont = null;
    try {
      cont = (ResourceContainerImpl)container.lock();
      File prop = cont.getStore().getResourceFile(name);
      if (prop.exists()) prop.delete();
    } finally {
      container.unlock();
    }
  }

  public void init(ResourceSpace space, Properties P) 
    throws ResourceSaverInitException
  {
    this.space = (ResourceSpaceImpl)space;
    
    this.defaultClass = (String)P.get(DEFAULTCLASS_P);
    if (defaultClass == null)
      throw new ResourceSaverInitException("no "+DEFAULTCLASS_P+" prop.");
  }

  public PropertyHolder getPropertyHolder(Runner run, Resource r) {
    return new PropertyHolderImpl(run,r);
  }

  public PropertyResourceSaver() {}

  public PropertyResourceSaver (ResourceSpace space, String defaultClass)  {
    this.space = (ResourceSpaceImpl)space;
    this.defaultClass = defaultClass;
  }

}
