/*
 * Copyright 2006-2011 the Seasar Foundation and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * 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 org.seasar.codegen.impl;

import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.seasar.codegen.CodeGenConfig;
import org.seasar.codegen.ImportCodeData;
import org.seasar.codegen.convert.FKNameConverter;
import org.seasar.codegen.dbms.Dbms;
import org.seasar.codegen.element.DataType;
import org.seasar.codegen.element.Field;
import org.seasar.codegen.element.FieldSetting;
import org.seasar.codegen.element.LinkTable;
import org.seasar.codegen.element.PrimaryKey;
import org.seasar.codegen.element.Table;
import org.seasar.codegen.exception.InternalGenerateException;
import org.seasar.codegen.exception.NotTypeMatchException;
import org.seasar.codegen.util.CreateTableTypeToTypeUtil;
import org.seasar.codegen.util.ExcelUtil;
import org.seasar.codegen.util.IdentityUtil;
import org.seasar.codegen.util.LinkUtil;
import org.seasar.framework.container.annotation.tiger.Binding;
import org.seasar.framework.container.annotation.tiger.BindingType;
import org.seasar.framework.util.StringUtil;

/**
 * Excel のテーブル定義書読み込み基底クラス。
 * 
 * @author glad
 */
public abstract class ExcelImportCodeDataSupport implements ImportCodeData {

    protected FKNameConverter converter;

    protected int attributeNameCol;

    protected int columnNameCol;

    protected int dataTypeCol;

    protected int notNullCol;

    protected int primaryKeyCol;

    protected int foreignKeyCol;

    protected int defaultValueCol;

    protected int relationTypeCol;

    protected int parentTableCol;

    protected int parentColumnCol;

    protected int cardinalityCol;

    protected int parentPropertyCol;

    protected int childPropertyCol;

    protected Dbms dataTypeSelectUtil;

    protected static final String IDENTITY = "identity";

    protected static final String SEQUENCE = "sequence";

    @Binding(bindingType = BindingType.MUST)
    protected CodeGenConfig codeGenConfig;

    public void setDbms(Dbms dataTypeSelectUtil) {
        this.dataTypeSelectUtil = dataTypeSelectUtil;
    }

    protected Field getField(HSSFRow row, Table table) {
        String columnName = getString(row, columnNameCol);
        if (StringUtil.isEmpty(columnName)) {
            return null;
        }
        Field field = new Field();
        field.setFieldName(columnName);
        try {
            field.setDataType(getDataType(row));
        } catch (InternalGenerateException e) {
            throw new NotTypeMatchException(table.getTableName(), columnName, e);
        }
        field.setFieldAttributeName(getString(row, attributeNameCol));
        return field;
    }

    protected DataType getDataType(HSSFRow row)
            throws InternalGenerateException {
        String dataType = getString(row, dataTypeCol);
        String notNull = getString(row, notNullCol);
        String defaultValue = getString(row, defaultValueCol);
        return getDataType(dataType, notNull, defaultValue);
    }

    protected DataType getDataType(String dataType, String notNull,
            String defaultValue) throws InternalGenerateException {
        FieldSetting fieldSetting = new FieldSetting();
        String typeName = CreateTableTypeToTypeUtil.getTypeName(dataType);
        String langType = dataTypeSelectUtil.convDBTypeToDataType(typeName);
        fieldSetting.setTypeName(langType);
        String length = CreateTableTypeToTypeUtil.getLength(dataType, typeName);
        int precision = CreateTableTypeToTypeUtil.getFullLength(length);
        fieldSetting.setColmnSize(precision);
        int scale = CreateTableTypeToTypeUtil.getPointNumberLength(length);
        fieldSetting.setPointNumber(scale);
        boolean isNotNull = "NOT NULL".equalsIgnoreCase(notNull);
        fieldSetting.setNotNull(isNotNull);
        fieldSetting.setFieldDefault(defaultValue);
        DataType type = dataTypeSelectUtil.selectBestDataType(fieldSetting);
        return type;
    }

    protected PrimaryKey getPrimaryKey(HSSFRow row, Field field) {
        String isPrimaryKey = getString(row, primaryKeyCol);
        if (StringUtil.isEmpty(isPrimaryKey)) {
            return null;
        } else {
            isPrimaryKey = isPrimaryKey.trim();
        }
        // ExcelでIdentityが指定
        if (isPrimaryKey.equalsIgnoreCase(IDENTITY)) {
            PrimaryKey primaryKey = new PrimaryKey();
            primaryKey.setField(field);
            field.setUseIdentity(true);
            return primaryKey;
        }
        // Excelでシーケンスが指定
        if (isPrimaryKey.startsWith("\"") && isPrimaryKey.endsWith("\"")) {
            PrimaryKey primaryKey = new PrimaryKey();
            primaryKey.setField(field);
            field.setSequence(isPrimaryKey.replaceAll("\"", ""));
            return primaryKey;
        }
        // Excelで「OKないしtrue」
        if (IdentityUtil.isTrue(isPrimaryKey)) {
            PrimaryKey primaryKey = new PrimaryKey();
            primaryKey.setField(field);
            if (codeGenConfig.getIdentityType() != null) {
                if (IdentityUtil.isIdentityConfig(codeGenConfig
                        .getIdentityType().trim())) {
                    field.setUseIdentity(true);
                } else if (IdentityUtil.isSequenceConfig(codeGenConfig
                        .getIdentityType())) {
                    field.setSequence(field.getFieldNameForDto());
                }
                return primaryKey;
            } else {
                field.setSequence(field.getFieldNameForDto());
                return primaryKey;
            }
        }
        return null;
    }

    protected LinkTable getParentLink(HSSFRow row, Field field) {
        String relationType = getString(row, relationTypeCol);
        if (StringUtil.isEmpty(relationType)) {
            return null;
        }
        LinkTable link = new LinkTable();
        link.setTableName(getString(row, parentTableCol));
        link.setParentFieldName(getString(row, parentColumnCol));
        link.setChildFieldName(field.getFieldName());
        return link;
    }

    protected void setupChildLinks(Map<String, Table> tableMap) {
        LinkUtil.setupChildLinks(tableMap);
    }

    protected String getString(HSSFSheet sheet, int row, int col) {
        return ExcelUtil.getString(sheet, row, col);
    }

    protected String getString(HSSFRow row, int col) {
        return ExcelUtil.getString(row, col);
    }

    /**
     * @param codeGenConfig
     *            設定する codeGenConfig。
     */
    public void setCodeGenConfig(CodeGenConfig codeGenConfig) {
        this.codeGenConfig = codeGenConfig;
    }

    public void setFKNameConverter(FKNameConverter converter) {
        this.converter = converter;
    }
}
