/*
 * blancoDb
 * Copyright (C) 2004-2005 Yasuo Nakanishi
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.db.generator;

import java.io.IOException;
import java.util.Iterator;

import blanco.db.conf.BlancoDbSetting;
import blanco.db.definition.BlancoDbDefinition;
import blanco.db.definition.QueryInvoker;
import blanco.db.definition.QueryIterator;
import blanco.db.definition.TableGateway;
import blanco.db.exception.IntegrityConstraintException;
import blanco.db.exception.NoRowFoundException;
import blanco.db.exception.NoRowModifiedException;
import blanco.db.exception.NotSingleRowException;
import blanco.db.exception.TooManyRowsFoundException;
import blanco.db.exception.TooManyRowsModifiedException;
import blanco.db.expander.query.invoker.QueryInvokerClass;
import blanco.db.expander.query.iterator.QueryIteratorClass;
import blanco.db.expander.table.gateway.TableGatewayClass;
import blanco.db.expander.table.row.RowObjectClass;
import blanco.db.properties.GenerationProperties;
import blanco.db.util.BlancoDbObjectStorage;
import blanco.db.util.BlancoDbUtil;
import blanco.ig.expander.ClassExpander;
import blanco.ig.expander.Type;
import blanco.ig.generator.Generator;
import blanco.ig.generator.ImplementGenerator;
import blanco.ig.generator.MasterGenerator;
import blanco.ig.generator.RuntimeGenerator;
import blanco.ig.service.ServiceClass;

/**
 * @author Yasuo Nakanishi
 */
public class BlancoDbGenerator implements Generator {
    private BlancoDbSetting _setting = null;

    private ImplementGenerator _generator = null;

    private TypeFactory _typeFactory = null;

    public BlancoDbGenerator(BlancoDbSetting setting) {
        _setting = setting;
    }

    private void setupStorage() {
        BlancoDbObjectStorage storage = BlancoDbObjectStorage.getInstance();
        storage.regist("Setting", _setting);
        storage.regist("GenerationProperties", new GenerationProperties());
        storage.regist("TypeFactory", new TypeFactory(_setting
                .getRootNameSpace()));
    }

    /**
     * TableGateway以外を処理します。
     * 
     * @param definition
     */
    public void setup(BlancoDbDefinition definition) {
        ServiceClass serviceClass = new ServiceClass("Db", definition.getName());
        BlancoDbObjectStorage.initialize(serviceClass);

        setupStorage();
        BlancoDbObjectStorage storage = BlancoDbObjectStorage.getInstance();
        _typeFactory = storage.getTypeFactory();

        _generator = new ImplementGenerator(serviceClass, _setting);
        MasterGenerator.get().regist(_generator);

        setupQueryIteratorExpander(definition);
        setupQueryInvokerExpander(definition);
    }

    /**
     * TableGatewayのみ作成します。
     * 
     * @param definition
     */
    public void setupTableGateway(BlancoDbDefinition definition) {
        ServiceClass serviceClass = new ServiceClass("Db", definition.getName());
        BlancoDbObjectStorage.initialize(serviceClass);

        setupStorage();
        BlancoDbObjectStorage storage = BlancoDbObjectStorage.getInstance();
        _typeFactory = storage.getTypeFactory();

        _generator = new ImplementGenerator(serviceClass, _setting);
        MasterGenerator.get().regist(_generator);

        setupTableGatewayExpander(definition);
    }

    public void generateRuntime() throws IOException {
        RuntimeGenerator generator = new RuntimeGenerator(_setting);
        generator.addSourceDirectory("src/main");
        generator.addRuntime(BlancoDbUtil.class);
        generator.addRuntime(IntegrityConstraintException.class);
        generator.addRuntime(NoRowFoundException.class);
        generator.addRuntime(NoRowModifiedException.class);
        generator.addRuntime(NotSingleRowException.class);
        generator.addRuntime(TooManyRowsFoundException.class);
        generator.addRuntime(TooManyRowsModifiedException.class);
        generator.addPackagePair("blanco.db.util", _setting.getRootNameSpace()
                + ".util");
        generator.addPackagePair("blanco.db.exception", _setting
                .getRootNameSpace()
                + ".exception");
        generator.generate();
    }

    private void setupTableGatewayExpander(BlancoDbDefinition definition) {

        TableGateway gateway = null;
        RowObjectClass roc = null;
        for (int i = 0; i < definition.getTableGatewayCount(); i++) {
            gateway = definition.getTableGateway(i);

            roc = createRowObjectClass(gateway);
            _generator.addMain(roc);

            if (gateway.exsistPrimaryKey()) {
                _generator.addMain(createTableGatewayClass(gateway));
            } else {
                String message = "警告:テーブル、メタ情報の両方にプライマリキーが設定されていないので、";
                message += "TableGatewayの作成を中止しました。テーブル名:";
                message += gateway.getDbName();
                System.out.println(message);
            }

        }
    }

    private void setupQueryIteratorExpander(BlancoDbDefinition definition) {

        QueryIterator queryIterator = null;
        RowObjectClass roc = null;
        Iterator iterator = definition.getQueryIteratorIterator();

        while (iterator.hasNext()) {
            queryIterator = (QueryIterator) iterator.next();

            roc = createRowObjectClass(queryIterator);
            _generator.addMain(roc);

            _generator.addMain(createQueryIterator(queryIterator));
        }
    }

    private void setupQueryInvokerExpander(BlancoDbDefinition definition) {

        QueryInvoker invoker = null;
        Iterator iterator = definition.getQueryInvokerIterator();
        while (iterator.hasNext()) {
            invoker = (QueryInvoker) iterator.next();
            _generator.addMain(createQueryInvoker(invoker));

        }
    }

    private RowObjectClass createRowObjectClass(TableGateway gateway) {
        Type type = _typeFactory
                .createRowObject(gateway.getClassName() + "Row");
        RowObjectClass expander = new RowObjectClass(type, gateway);
        return expander;
    }

    private ClassExpander createQueryIterator(QueryIterator iterator) {
        Type type = _typeFactory.createQueryIterator(iterator.getName());
        ClassExpander expander = new QueryIteratorClass(type, iterator);
        return expander;
    }

    private ClassExpander createQueryInvoker(QueryInvoker invoker) {
        Type type = _typeFactory.createQueryInvoker(invoker.getName());
        ClassExpander expander = new QueryInvokerClass(type, invoker);
        return expander;
    }

    private RowObjectClass createRowObjectClass(QueryIterator queryIterator) {
        Type type = _typeFactory.createRowObject(queryIterator.getName()
                + "Row");
        RowObjectClass expander = new RowObjectClass(type, queryIterator);
        return expander;
    }

    private ClassExpander createTableGatewayClass(TableGateway gateway) {
        Type type = _typeFactory.createTableGateway(gateway.getClassName());
        ClassExpander classExpander = new TableGatewayClass(type, gateway);
        return classExpander;
    }

    public void generate() throws IOException {
        generateRuntime();
        generateDataStruct();
        generateMain();
    }

    public void generateDataStruct() throws IOException {
        _generator.generateDataStruct();
    }

    public void generateMain() throws IOException {
        _generator.generateMain();
    }

    public void generateTest() throws IOException {
        // 何もしません。
    }
}