/*
 * Copyright (c) 1998-2008 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 Scott Ferguson
 */

package com.caucho.amber.gen;

import com.caucho.amber.field.*;
import com.caucho.amber.type.*;
import com.caucho.java.JavaWriter;
import com.caucho.util.L10N;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;

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

  public EntityComponent()
  {
  }

  /**
   * Gets the entity type.
   */
  @Override
  public EntityType getEntityType()
  {
    return (EntityType) _entityType;
  }

  /**
   * Sets the bean info for the generator
   */
  public void setEntityType(EntityType entityType)
  {
    setRelatedType(entityType);
  }

  /**
   * Generates the delete
   */
  void generateDelete(JavaWriter out)
    throws IOException
  {
    out.println();
    out.println("public void __caucho_delete()");
    out.println("{");
    out.pushDepth();

    out.println("if (com.caucho.amber.entity.EntityState.P_DELETING.ordinal() <= __caucho_state.ordinal())");
    out.println("  return;");
    out.println();

    out.println("if (__caucho_home != null)");
    out.println("  __caucho_home.preRemove(this);");
    out.println();

    generateCallbacks(out, "this", _entityType.getPreRemoveCallbacks());

    _entityType.generatePreDelete(out);

    out.println("__caucho_state = com.caucho.amber.entity.EntityState.P_DELETING;");

    out.println("if (__caucho_session != null) {");
    out.pushDepth();
    out.println("__caucho_session.update((com.caucho.amber.entity.Entity) this);");
    out.println("__caucho_home.getTable().beforeEntityDelete(__caucho_session, (com.caucho.amber.entity.Entity) this);");
    out.println("__caucho_state = com.caucho.amber.entity.EntityState.P_DELETED;");
    _entityType.generatePostDelete(out);
    out.popDepth();
    out.println("}");
    out.println("else");
    out.println("  __caucho_state = com.caucho.amber.entity.EntityState.P_DELETED;");

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

    Id id = _entityType.getId();

    out.println();
    out.println("private void __caucho_delete_int()");
    out.println("  throws java.sql.SQLException");
    out.println("{");
    out.pushDepth();

    // jpa/0ge2: MappedSuperclassType
    if ((_entityType.getTable() == null) || (id == null)) {
      out.println("return;");

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

      return;
    }

    out.println("java.sql.PreparedStatement pstmt = null;");
    out.println("String sql = null;");
    out.println();

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

    out.print("__caucho_home.delete(__caucho_session, ");
    out.print(id.toObject(id.generateGet("this")));
    out.println(");");

    out.println("__caucho_session.removeEntity((com.caucho.amber.entity.Entity) this);");

    String table = _entityType.getTable().getName();
    String where = _entityType.getId().generateMatchArgWhere(null);

    String sql = ("delete from " + table + " where " + where);

    out.print("sql = \"");
    out.printJavaString(sql);
    out.println("\";");

    out.println();
    out.println("pstmt = __caucho_session.prepareStatement(sql);");

    out.println("int index = 1;");
    id.generateSet(out, "pstmt", "index", "this");

    out.println();
    out.println("pstmt.executeUpdate();");

    out.println("__caucho_home.postRemove(this);");

    generateCallbacks(out, "this", _entityType.getPostRemoveCallbacks());

    out.popDepth();
    out.println("} catch (Exception e) {");
    out.println("  if (pstmt != null)");
    out.println("    __caucho_session.closeStatement(sql);");
    out.println();
    out.println("  if (e instanceof java.sql.SQLException)");
    out.println("    throw (java.sql.SQLException) e;");
    out.println();
    out.println("  if (e instanceof RuntimeException)");
    out.println("    throw (RuntimeException) e;");
    out.println();
    out.println("  throw new com.caucho.amber.AmberRuntimeException(e);");
    out.println("}");

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

  /**
   * Generates the flush
   */
  void generateFlush(JavaWriter out)
    throws IOException
  {
    out.println();
    out.println("protected void __caucho_flush_callback()");
    out.println("  throws java.sql.SQLException");
    out.println("{");
    out.println("}");

    out.println();
    out.println("public boolean __caucho_flush()");
    out.println("  throws java.sql.SQLException");
    out.println("{");
    out.pushDepth();

    boolean isAbstract = (_entityType.getBeanClass().isAbstract()
                          && _entityType.getPersistenceUnit().isJPA());

    if (_entityType.getId() == null || isAbstract) {
      // jpa/0ge6: MappedSuperclass

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

      return;
    }

    out.println("if (__caucho_session == null)");
    out.println("  return false;");
    out.println();

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

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

      if (field.isCascadable()) {
        CascadableField cascadable = (CascadableField) field;

        cascadable.generateFlushCheck(out);

        out.println();
      }
    }

    out.println();
    out.println("if (__caucho_state == com.caucho.amber.entity.EntityState.P_DELETED) {");
    out.println("  __caucho_delete_int();");
    out.println("  return true;");
    out.println("}");
    out.println("else if (__caucho_state == com.caucho.amber.entity.EntityState.P_PERSISTING) {");
    // jpa/0ga2
    out.println("  __caucho_create(__caucho_session, __caucho_home);");
    out.println("}");
    out.println("else if (__caucho_state == com.caucho.amber.entity.EntityState.P_PERSISTED) {");
    out.println("  __caucho_cascadePrePersist(__caucho_session);");
    out.println("}");
    out.println();

    out.println("boolean isDirty = false;");

    int dirtyCount = _entityType.getDirtyIndex();

    for (int i = 0; i <= dirtyCount / 64; i++) {
      out.println("long mask_" + i + " = __caucho_dirtyMask_" + i + ";");
      out.println("__caucho_dirtyMask_" + i + " = 0L;");
      out.println("__caucho_updateMask_" + i + " |= mask_" + i + ";");

      out.println();
      out.println("if (mask_" + i + " != 0L)");
      out.println("  isDirty = true;");
    }

    out.println();

    // if (version == null)
    out.println("if (isDirty) {");
    out.pushDepth();

    // ejb/0605
    out.println();
    out.println("__caucho_flush_callback();");

    // else {
    // jpa/0x02
    //  out.println("if (! (isDirty || " + version.generateIsNull() + "))");
    // }
    // out.println("  return true;");

    // jpa/0r10
    out.println("__caucho_home.preUpdate(this);");

    // jpa/0r10
    generateCallbacks(out, "this", _entityType.getPreUpdateCallbacks());

    out.println("com.caucho.util.CharBuffer cb = new com.caucho.util.CharBuffer();");

    out.println("__caucho_home.generateUpdateSQLPrefix(cb);");

    out.println("boolean isFirst = true;");

    VersionField version = _entityType.getVersionField();

    for (int i = 0; i <= dirtyCount / 64; i++) {
      // jpa/0x02 is a negative test.
      if (i != 0 || version == null) {
        out.println("if (mask_" + i + " != 0L)");
        out.print("  ");
      }

      out.println("isFirst = __caucho_home.generateUpdateSQLComponent(cb, " + i + ", mask_" + i + ", isFirst);");
    }
    out.println("__caucho_home.generateUpdateSQLSuffix(cb);");

    out.println();
    out.println("java.sql.PreparedStatement pstmt = null;");
    out.println("String sql = cb.toString();");
    out.println();
    out.println("try {");
    out.pushDepth();

    out.println("pstmt = __caucho_session.prepareStatement(sql);");

    out.println("int index = 1;");

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

      field.generateUpdate(out, "mask", "pstmt", "index");
    }

    out.println();
    _entityType.getId().generateStatementSet(out, "pstmt", "index");

    if (version != null) {
      out.println();
      version.generateStatementSet(out, "pstmt", "index");
    }

    out.println();
    out.println("int updateCount = pstmt.executeUpdate();");
    out.println();

    if (version != null) {
      out.println("if (updateCount == 0) {");
      out.println("  throw new javax.persistence.OptimisticLockException((com.caucho.amber.entity.Entity) this);");
      out.println("} else {");
      out.pushDepth();
      String value = version.generateGet("super");
      Type type = version.getColumn().getType();
      out.println(version.generateSuperSetter("this", type.generateIncrementVersion(value)) + ";");
      out.popDepth();
      out.println("}");
      out.println();
    }

    out.println("__caucho_home.postUpdate(this);");

    generateCallbacks(out, "this", _entityType.getPostUpdateCallbacks());

    out.println();
    generateLogFine(out, " amber update");

    out.println();
    out.println("__caucho_inc_version = false;");
    out.println();

    out.popDepth();
    out.println("} catch (Exception e) {");
    out.println("  if (pstmt != null)");
    out.println("    __caucho_session.closeStatement(sql);");
    out.println();
    out.println("  if (e instanceof java.sql.SQLException)");
    out.println("    throw (java.sql.SQLException) e;");
    out.println();
    out.println("  if (e instanceof RuntimeException)");
    out.println("    throw (RuntimeException) e;");
    out.println();
    out.println("  throw new com.caucho.amber.AmberRuntimeException(e);");
    out.println("}");

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

    out.println("if (__caucho_state == com.caucho.amber.entity.EntityState.P_PERSISTED) {");
    out.println("  __caucho_cascadePostPersist(__caucho_session);");
    out.println("}");

    out.println();
    out.println("return false;");

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