/*
 * Copyright (c) 2006, KNOPFLERFISH project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials
 *   provided with the distribution.
 *
 * - Neither the name of the KNOPFLERFISH project nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.knopflerfish.bundle.component;



import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import org.osgi.framework.Bundle;

import org.osgi.service.component.ComponentConstants;

// Björn should we merge this with Component?

class Config {

  private String name;
  private String implementation;
  private String factory;

  private boolean enabled;
  private boolean autoEnabled;
  private boolean serviceFactory;
  private boolean immediate;
  private boolean registerService;

  private Hashtable properties;
  private ArrayList references;
  private ArrayList services;
  private Component component;
  private Bundle bundle;

  public Config(Bundle bundle) {
    this.bundle = bundle;
    properties = new Hashtable();
    references = new ArrayList();
    services   = new ArrayList();
    registerService = true;
  }

  public void enable() {
    enable(null);
  }

  public synchronized void enable(Dictionary overriddenProps) {
    for (Iterator iter = references.iterator(); iter.hasNext();) {
      ((Reference) iter.next()).open();
    }
    
    if (component == null) // is this safe?
      createComponent(overriddenProps);
    
    SCR.getInstance().initComponent(component);
    
    enabled = true;
    referenceSatisfied();
    
  }

  public synchronized void disable() {
    enabled = false;
    
    if (component != null) {
      SCR.getInstance().removeComponent(component);
    }
   
    referenceUnsatisfied();
    for (Iterator iter = references.iterator(); iter.hasNext();) {
      ((Reference) iter.next()).close();
    }
  }

  public Component createComponent() {
    return createComponent(null);
  }

  public Component createComponent(Dictionary overriddenProps) {
    if (getFactory() != null) {      
      component = new FactoryComponent(this, overriddenProps);

    } else if (isServiceFactory()) {
      component = new ServiceFactoryComponent(this, overriddenProps);
      
    } else if (isImmediate() || services.isEmpty()) {
      component = new ImmediateComponent(this, overriddenProps);

    } else if (!isImmediate() && !services.isEmpty()){
      component = new DelayedComponent(this, overriddenProps);    
    } else {
      throw new RuntimeException("This is a bug and should not be happening.");
    }

    return component;
  }

  public boolean isSatisfied() {
    if (!isEnabled()) return false;
    
    for (int i = 0; i < references.size(); i++) {
      Reference ref = (Reference)references.get(i);
      if (!ref.isSatisfied()) return false;
    }

    return true;
  }

  public boolean getShouldRegisterService() {
    return services.size() > 0 && registerService;
  }
  
  public void setShouldRegisterService(boolean registerService) {
    this.registerService = registerService;
  }


  public void referenceSatisfied() {

    if (isSatisfied() && component != null) {
      component.satisfied();
    }
  }
  
  public void referenceUnsatisfied() {
    if (!isSatisfied() && component != null) {
      component.unsatisfied();
    }
  }
  
  public void bindReferences(Object instance) {
    for (int i = 0; i < references.size(); i++) {
      ((Reference) references.get(i)).bind(instance);
    }
  }

  public void unbindReferences(Object instance) {
    for (int i = references.size() - 1; i >= 0; i--) {
      ((Reference) references.get(i)).unbind(instance);
    }
  }

  public String[] getServices() {
    if (services.size() == 0)
      return null; // HEY, this might be dangerous
    
    String[] ret = new String[services.size()];
    services.toArray(ret);
    return ret;
  }

  public Dictionary getProperties() {
    return properties;
  }

  public void setProperty(String key, Object value) {
    properties.put(key, value);
  }

  public Bundle getBundle() {
    return bundle;
  }

  public String getName() {
    return name;
  }

  public boolean isEnabled() {
    return enabled;
  }

  public boolean isAutoEnabled() {
    return autoEnabled;
  }

  public String getImplementation() {
    return implementation;
  }

  public boolean isImmediate() {
    return immediate;
  }

  public boolean isServiceFactory() {
    return serviceFactory;
  }

  public String getFactory() {
    return factory;
  }

  protected ArrayList getReferences() {
    return references;
  }
  
  public Reference getReference(String name) {

    for (int i = 0; i < references.size(); i++) {
      Reference ref = (Reference)references.get(i);
      
      if (name.equals(ref.getName())) {
        return ref;
      }
    }

    return null;
  }

  /** 
      Is this really safe? What should happen when
      CM overrides a factory's properties? Should these
      changes be "inherited" instances created by the factory?
      
      
     Overrides properties according to 112.6 
     i.e avoids changing component.name and component.id
  */
  public void overrideProperties(Dictionary overriddenProps) {
    
    for (Enumeration e = overriddenProps.keys();
         e.hasMoreElements(); ) {
      String key = (String)e.nextElement();
      if (!key.equals(ComponentConstants.COMPONENT_NAME) &&
          !key.equals(ComponentConstants.COMPONENT_ID)) {
        setProperty(key, overriddenProps.get(key));
      }
    }  
  }

  public void addReference(Reference ref) {
    ref.setConfig(this);
    references.add(ref);
  }

  public void addService(String interfaceName) {
    services.add(interfaceName);
  }

  public void setAutoEnabled(boolean autoEnabled) {
    this.autoEnabled = autoEnabled;
  }

  public void setImplementation(String impl) {
    implementation = impl;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setServiceFactory(boolean isServiceFactory) {
    serviceFactory = isServiceFactory;
  }

  public void setFactory(String factory) {
    this.factory = factory;
  }

  public void setImmediate(boolean isImmediate) {
    immediate = isImmediate;
  }

  public Config copy() {
    Config config = new Config(bundle);
    for (Enumeration e = properties.keys(); e.hasMoreElements();) {
      String key = (String) e.nextElement();
      config.setProperty(key, properties.get(key)); // TODO: Is this ok? Clone value?
    }
    for (Iterator iter = references.iterator(); iter.hasNext();) {
      config.addReference(((Reference) iter.next()).copy());
    }
    for (Iterator iter = services.iterator(); iter.hasNext();) {
      config.addService((String) iter.next());
    }
    config.setAutoEnabled(autoEnabled);
    config.setImplementation(implementation);
    config.setName(name);
    config.setServiceFactory(serviceFactory);
    config.setFactory(factory);
    config.setImmediate(immediate);
    return config;
  }
}

