/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.tools.config;

import com.queplix.core.jxb.entity.types.ControlSType;
import com.queplix.core.modules.config.actions.EntityInstallationAction;
import com.queplix.core.modules.config.utils.EntityDdobjectInfoVO;
import com.queplix.core.modules.config.utils.EntityFieldInfoVO;
import com.queplix.core.modules.config.utils.EntitySchema;
import com.queplix.core.modules.config.utils.EntitySchemaFactory;
import com.queplix.core.tools.AbstractTool;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * <p>Generates Entity stubs</p>
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:31:14 $
 */

public class EntityGenerationTool
        extends AbstractTool {

    public static final String DEF_DEFSRC = "nextkey('$dbobject')";
    public static final ControlSType DEF_CONTROL = ControlSType.EDIT;

    private String outdir;
    private String entitySchema;
    private boolean overwriteEntity;

    private File outDir;
    private File outDsDir;

    //
    // Call action
    //
    public void callAction(Map params)
            throws Exception {

        //
        // Read parameters
        //

        boolean allTables = ((Boolean) params.get("all")).booleanValue();
        String entityName = (String) params.get("entity");
        String dbobjectName = (String) params.get("dbobject");

        this.outdir = (String) params.get("outdir");
        this.entitySchema = (String) params.get("schema");
        this.overwriteEntity = ((Boolean) params.get("overwrite"))
                .booleanValue();

        //
        // Initialization.
        //

        if(outdir != null) {
            outDir = new File(outdir);
            if(!outDir.exists() || !outDir.isDirectory()) {
                throw new IllegalStateException("Bad entity output dir: " +
                        outDir.getAbsolutePath());
            }

            outDsDir = new File(outDir,
                    EntityInstallationAction.DATASCHEMA_SUB_FOLDER);
            if(!outDsDir.exists() || !outDsDir.isDirectory()) {
                throw new IllegalStateException(
                        "Bad entity dataschema output dir: " +
                                outDsDir.getAbsolutePath());
            }
        }

        //
        // Generate entity(s)
        //

        if(!allTables) {
            if(entityName == null) {
                throw new NullPointerException("Parameter XML dir is NULL");
            }
            if(dbobjectName == null) {
                throw new NullPointerException("Parameter XSL dir is NULL");
            }

            EntityDdobjectInfoVO dbobjectInfo = getEntityDdobjectInfo(
                    dbobjectName);
            generateEntity(entityName, dbobjectInfo);

        } else {

            Collection col = getEntityDdobjectsInfo();
            generateEntities(col);
        }
    }

    //
    // Generate new entities XML and write to the disk/screen
    //
    private void generateEntities(Collection col)
            throws Exception {

        if(col == null) {
            throw new NullPointerException("No any dbobjects found.");
        }

        int i = 0;
        for(Iterator it = col.iterator(); it.hasNext();) {
            EntityDdobjectInfoVO dbobjectInfo = (EntityDdobjectInfoVO) it
                    .next();
            String dbobjectName = dbobjectInfo.getDbobjectName();
            String entityName = dbobjectName.toLowerCase();

            INFO("#" + i + " generating entity '" + entityName +
                    "' for dbobject '" + dbobjectName + "'...");

            generateEntity(entityName, dbobjectInfo);
            i++;
        }
    }

    //
    // Generate new entity XML and write to the disk/screen
    //
    private void generateEntity(String entityName,
                                EntityDdobjectInfoVO dbobjectInfo)
            throws Exception {

        String dbobjectName = dbobjectInfo.getDbobjectName();

        //
        // 1) Init EntityConfig and Entity elements
        //

        StringBuffer entitySb = new StringBuffer();
        entitySb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        entitySb.append(
                "<entity-config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
        entitySb.append("xsi:noNamespaceSchemaLocation=\"dtd/entity.xsd\">\n");
        entitySb.append("\t<entity name=\"").append(entityName).append("\" ");
        entitySb.append("dbobject=\"").append(dbobjectName).append("\">\n");

        {
            // Init Efield elements

            for(int i = 0; i < dbobjectInfo.getFieldSize(); i++) {
                EntityFieldInfoVO fieldInfo = dbobjectInfo.getField(i);
                String column = fieldInfo.getDbcolumnName();

                entitySb.append("\t\t<efield name=\"").append(
                        column.toLowerCase()).append("\" ");
                entitySb.append("control=\"").append(DEF_CONTROL.toString())
                        .append("\"");

                // .. set pkey
                boolean isPkey = false;
                for(int j = 0; j < dbobjectInfo.getPkeySize(); j++) {
                    String pkey = dbobjectInfo.getPkey(j);
                    if(column.equals(pkey)) {
                        isPkey = true;
                        break;
                    }
                }

                if(isPkey) {
                    entitySb.append(" pkey=\"true\">\n");
                    entitySb.append("\t\t\t<eql-defsrc>").append(DEF_DEFSRC)
                            .append("</eql-defsrc>\n");
                    entitySb.append("\t\t</efield>\n");
                } else {
                    entitySb.append("/>\n");
                }
            }
        }

        // Complete EntityConfig and Entity elements initialization

        entitySb.append("\t</entity>\n");
        entitySb.append("</entity-config>\n");

        //
        // 2) Init Dataschemas element
        //

        StringBuffer dataschemaSb = new StringBuffer();
        int fkKeys = dbobjectInfo.getFkeySize();

        if(fkKeys > 0) {

            dataschemaSb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            dataschemaSb.append(
                    "<dataschemas xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
            dataschemaSb.append(
                    "xsi:noNamespaceSchemaLocation=\"../dtd/dataschema.xsd\">\n");

            for(int i = 0; i < fkKeys; i++) {
                EntityDdobjectInfoVO.FK fk = dbobjectInfo.getFkey(i);
                String pkColumn = fk.getPkDbcolumnName();
                String fkDbobject = fk.getFkDbobjectName();
                String fkColumn = fk.getFkDbcolumnName();

                String dataschemaName = (dbobjectName + "#" + fkDbobject)
                        .toLowerCase();

                // Init Dataschema and Table elements

                dataschemaSb.append("\t<dataschema name=\"").append(
                        dataschemaName).append("\" ");
                dataschemaSb.append("reftype=\"1:n\">\n");

                dataschemaSb.append("\t\t<table name=\"").append(dbobjectName)
                        .append("\">\n");
                dataschemaSb.append("\t\t\t<key>").append(pkColumn).append(
                        "</key>\n");
                dataschemaSb.append("\t\t</table>\n");
                dataschemaSb.append("\t\t<table name=\"").append(fkDbobject)
                        .append("\">\n");
                dataschemaSb.append("\t\t\t<key>").append(fkColumn).append(
                        "</key>\n");
                dataschemaSb.append("\t\t</table>\n");

                dataschemaSb.append("\t</dataschema>\n");
            }

            // Complete Dataschemas element initialization
            dataschemaSb.append("</dataschemas>\n");
        }

        //
        // 3) Printing out entity
        //

        Writer w = null;
        try {
            if(outDir != null) {
                // .. print to file
                File entityFile = new File(outDir, entityName + "." +
                        EntityInstallationAction.CONFIG_FILES_EXTENSION);

                if(!overwriteEntity && entityFile.exists()) {
                    throw new IllegalStateException(
                            "Entity file already exists: " +
                                    entityFile.getAbsolutePath());
                }
                w = new FileWriter(entityFile);

            } else {
                // .. print to stdout
                w = new PrintWriter(System.out);
            }

            w.write(entitySb.toString());
            w.flush();

        } finally {
            if(w != null) {
                w.close();
            }
        }

        //
        // 4) Printing out dataschema
        //

        if(dataschemaSb.length() > 0) {
            try {
                if(outDsDir != null) {
                    // .. print to file
                    File dataschemaFile = new File(outDsDir, entityName + "." +
                            EntityInstallationAction.CONFIG_FILES_EXTENSION);

                    if(!overwriteEntity && dataschemaFile.exists()) {
                        throw new IllegalStateException(
                                "Dataschema file already exists: " +
                                        dataschemaFile.getAbsolutePath());
                    }
                    w = new FileWriter(dataschemaFile);

                } else {
                    // .. print to stdout
                    w = new PrintWriter(System.out);
                }

                w.write(dataschemaSb.toString());
                w.flush();

            } finally {
                if(w != null) {
                    w.close();
                }
            }
        }
    }

    //
    // Get DB objects information
    //
    private Collection getEntityDdobjectsInfo() {
        EntitySchema es;
        if(entitySchema == null) {
            es = EntitySchemaFactory.getInstance().get();
        } else {
            es = EntitySchemaFactory.getInstance().get(entitySchema);
        }

        return es.getEntityDdobjectInfo();
    }

    //
    // Get DB object information
    //
    private EntityDdobjectInfoVO getEntityDdobjectInfo(String dbObject) {
        EntitySchema es;
        if(entitySchema == null) {
            es = EntitySchemaFactory.getInstance().get();
        } else {
            es = EntitySchemaFactory.getInstance().get(entitySchema);
        }

        return es.getEntityDdobjectInfo(dbObject);
    }

    //
    // Main method
    //
    public static void main(String[] args) {

        boolean allTables = false;
        String entityName = null;
        String dbObject = null;
        String outdir = null;
        String entitySchema = null;
        boolean overwriteEntity = false;

        // parse params
        for(int i = 0; i < args.length; i++) {
            String s = args[i];

            if(s.equals("-all")) {
                allTables = true;
            } else if(s.equals("-entity")) {
                entityName = args[++i];
            } else if(s.equals("-dbobject")) {
                dbObject = args[++i];
            } else if(s.equals("-outdir")) {
                outdir = args[++i];
            } else if(s.equals("-schema")) {
                entitySchema = args[++i];
            } else if(s.equals("-overwrite")) {
                overwriteEntity = true;
            }
        }

        // fill params
        Map params = new HashMap();
        params.put("all", new Boolean(allTables));
        params.put("entity", entityName);
        params.put("dbobject", dbObject);
        params.put("outdir", outdir);
        params.put("schema", entitySchema);
        params.put("overwrite", new Boolean(overwriteEntity));

        // start action
        try {
            new EntityGenerationTool().callAction(params);

        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("USAGE: java EntityGenerationTool " +
                    "-entity ENTITY_NAME -dbobject DBOBJECT " +
                    "[-outdir OUT_DIR] [-schema ENTITY_SCHEMA] [-overwrite]");
            System.out.println("..or..");
            System.out.println("		java EntityGenerationTool " +
                    "-all [-outdir OUT_DIR] [-schema ENTITY_SCHEMA] [-overwrite]");
        }
    }
}
