/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Rodrigo Westrupp
 */

package com.caucho.amber.gen;

import com.caucho.VersionFactory;
import com.caucho.amber.field.*;
import com.caucho.amber.type.EmbeddableType;
import com.caucho.bytecode.*;
import com.caucho.java.JavaWriter;
import com.caucho.java.gen.ClassComponent;
import com.caucho.util.L10N;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;

/**
 * Generates the Java code for the wrapped object.
 */
public class EmbeddableComponent extends ClassComponent {
  private static final L10N L = new L10N(EmbeddableComponent.class);

  private String _baseClassName;
  private String _extClassName;

  private EmbeddableType _embeddableType;

  public EmbeddableComponent()
  {
  }

  /**
   * Sets the bean info for the generator
   */
  public void setEmbeddableType(EmbeddableType embeddableType)
  {
    _embeddableType = embeddableType;
  }

  /**
   * Sets the base class name
   */
  public void setBaseClassName(String baseClassName)
  {
    _baseClassName = baseClassName;
  }

  /**
   * Gets the base class name
   */
  public String getBaseClassName()
  {
    return _baseClassName;
  }

  /**
   * Sets the ext class name
   */
  public void setExtClassName(String extClassName)
  {
    _extClassName = extClassName;
  }

  /**
   * Sets the ext class name
   */
  public String getClassName()
  {
    return _extClassName;
  }

  /**
   * Get bean class name.
   */
  public String getBeanClassName()
  {
    return _baseClassName;
  }

  /**
   * Starts generation of the Java code
   */
  @Override
  public void generate(JavaWriter out)
    throws IOException
  {
    try {
      generateHeader(out);

      generateInit(out);

      generateMake(out);
      
      generateMakeFromLoad(out);

      // jpa/0u21
      generateGetField(out);

      // generateFields(out);

      generateLoad(out);
    } catch (IOException e) {
      throw e;
    }
  }

  /**
   * Generates the class header for the generated code.
   */
  private void generateHeader(JavaWriter out)
    throws IOException
  {
    out.println("/*");
    out.println(" * Generated by Resin Amber");
    out.println(" * " + VersionFactory.getVersion());
    out.println(" */");
    out.println("private static final java.util.logging.Logger __caucho_log");
    out.println("  = java.util.logging.Logger.getLogger(\"" + getBeanClassName() + "\");");
  }

  /**
   * Generates the initialization.
   */
  private void generateInit(JavaWriter out)
    throws IOException
  {
    String className = getClassName();
    int p = className.lastIndexOf('.');
    if (p > 0)
      className = className.substring(p + 1);

    ArrayList<AmberField> fields = _embeddableType.getFields();

    for (Constructor ctor : _embeddableType.getBeanClass().getConstructors()) {
      out.println();
      // XXX: s/b actual access type?
      out.print("public ");

      out.print(className);
      out.print("(");

      Class []args = ctor.getParameterTypes();
      for (int i = 0; i < args.length; i++) {
        if (i != 0)
          out.print(", ");

        out.printClass(args[i]);
        out.print(" a" + i);
      }
      out.println(")");
      out.println("{");
      out.pushDepth();

      out.print("super(");
      for (int i = 0; i < args.length; i++) {
        if (i != 0)
          out.print(", ");

        out.print("a" + i);
      }
      out.println(");");

      for (AmberField field : fields) {
        field.generatePostConstructor(out);
      }

      out.popDepth();
      out.println("}");
    }
  }

  /**
   * Generates the fields.
   */
  /*
  private void generateFields(JavaWriter out)
    throws IOException
  {
    // jpa/0u22
    if (_embeddableType.isIdClass() && ! _embeddableType.isFieldAccess())
      return;

    ArrayList<AmberField> fields = _embeddableType.getFields();

    for (int i = 0; i < fields.size(); i++) {
      AmberField prop = fields.get(i);

      prop.generatePrologue(out, null);

      prop.generateSuperGetterMethod(out);
      prop.generateGetterMethod(out);

      prop.generateSuperSetterMethod(out);
      prop.generateSetterMethod(out);
    }
  }
  */

  /**
   * Generates the make method.
   */
  private void generateMake(JavaWriter out)
    throws IOException
  {
    ArrayList<AmberField> fields = _embeddableType.getFields();

    out.println();
    out.println("public static " + getClassName() + " __caucho_make(");
    
    for (int i = 0; i < fields.size(); i++) {
      AmberField prop = fields.get(i);

      out.print("  " + prop.getJavaTypeName() + " a" + i);
      
      if (i + 1 < fields.size())
        out.println(",");
    }

    out.println(")");
    
    out.println("{");
    out.pushDepth();

    out.println(getClassName() + " bean = new " + getClassName() + "();");
    
    for (int i = 0; i < fields.size(); i++) {
      AmberField prop = fields.get(i);

      out.println(prop.generateSuperSetter("bean", "a" + i) + ";");
    }

    out.println("return bean;");

    out.popDepth();
    out.println("}");
  }

  /**
   * Generates the make method.
   */
  private void generateMakeFromLoad(JavaWriter out)
    throws IOException
  {
    ArrayList<AmberField> fields = _embeddableType.getFields();

    out.println();
    out.println("public static " + getClassName() + " __caucho_make(");
    out.println("  com.caucho.amber.manager.AmberConnection aConn,");
    out.println("  java.sql.ResultSet rs,");
    out.println("  int index)");
    out.println("  throws java.sql.SQLException");
    
    out.println("{");
    out.pushDepth();

    out.println(getClassName() + " bean = new " + getClassName() + "();");

    out.println("bean.__caucho_load(aConn, rs, index);");

    out.println("return bean;");

    out.popDepth();
    out.println("}");
  }

  /**
   * Generates the get field method.
   */
  private void generateGetField(JavaWriter out)
    throws IOException
  {
    out.println();
    out.println("public Object __caucho_get_field(int index)");
    out.println("{");
    out.pushDepth();

    out.println("switch (index) {");
    out.pushDepth();

    ArrayList<AmberField> fields = _embeddableType.getFields();

    for (int i = 0; i < fields.size(); i++) {
      AmberField prop = fields.get(i);

      if (! (prop instanceof PropertyField))
        break;

      out.println("case " + i + ":");

      out.println("  return " + prop.generateSuperGetter("this") + ";");

      out.println();
    }

    out.println("default:");
    out.println("  throw new IllegalStateException(\"invalid index: \" + index);");

    out.popDepth();
    out.println("}");

    out.popDepth();
    out.println("}");
   }

  /**
   * Generates the load.
   */
  private void generateLoad(JavaWriter out)
    throws IOException
  {
    out.println();
    out.println("public int __caucho_load(com.caucho.amber.manager.AmberConnection aConn,");
    out.println("                         java.sql.ResultSet rs,");
    out.println("                         int index)");
    out.println("  throws java.sql.SQLException");
    out.println("{");
    out.pushDepth();

    if (_embeddableType.isIdClass()) {
      out.println("return 0;");
    }
    else {
      _embeddableType.generateLoad(out, "rs", "index", 0, -1);

      out.println("return index;");
    }

    out.popDepth();
    out.println("}");
  }
}
