/*
 * blanco Framework
 * Copyright (C) 2004-2006 IGA Tosiki
 * 
 * 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.cg.transformer.python;

import java.util.List;

import blanco.cg.valueobject.BlancoCgClass;
import blanco.cg.valueobject.BlancoCgField;
import blanco.cg.valueobject.BlancoCgLangDoc;
import blanco.cg.valueobject.BlancoCgMethod;
import blanco.cg.valueobject.BlancoCgSourceFile;
import blanco.cg.valueobject.BlancoCgType;

/**
 * BlancoCgClassをソースコードへと展開します。
 * 
 * このクラスはblancoCgのバリューオブジェクトからソースコードを自動生成するトランスフォーマーの個別の展開機能です。
 * 
 * @author IGA Tosiki
 */
class BlancoCgClassPythonSourceExpander {

    /**
     * ここでClassを展開します。
     * 
     * @param cgClass
     *            処理対象となるクラス。
     * @param argSourceLines
     *            ソースコード。
     */
    public void transformClass(final BlancoCgClass cgClass,
            final BlancoCgSourceFile argSourceFile, final List argSourceLines) {
        // 最初にクラス情報をLangDocに展開。
        if (cgClass.getLangDoc() == null) {
            // LangDoc未指定の場合にはこちら側でインスタンスを生成。
            cgClass.setLangDoc(new BlancoCgLangDoc());
        }
        if (cgClass.getLangDoc().getTitle() == null) {
            cgClass.getLangDoc().setTitle(cgClass.getDescription());
        }

        final StringBuffer buf = new StringBuffer();

        // if (BlancoStringUtil.null2Blank(cgClass.getAccess()).length() > 0) {
        // buf.append(cgClass.getAccess() + " ");
        // }
        // if (cgClass.getAbstract()) {
        // buf.append("abstract ");
        // }
        // if (cgClass.getFinal()) {
        // buf.append("final ");
        // }
        buf.append("class " + cgClass.getName());

        // 親クラスを展開。
        expandExtendClassList(cgClass, argSourceFile, buf);

        buf.append(":");

        // 行を確定して書き出しを実施。
        argSourceLines.add(buf.toString());

        // 次に LangDocをソースコード形式に展開。
        new BlancoCgLangDocPythonSourceExpander().transformLangDoc(cgClass
                .getLangDoc(), argSourceLines);

        // ここでフィールドを展開。
        expandFieldList(cgClass, argSourceFile, argSourceLines);

        // ここでメソッドを展開。
        expandMethodList(cgClass, argSourceFile, argSourceLines);

        // クラスのブロックの終了。
        argSourceLines.add("#end");
    }

    /**
     * 親クラスを展開します。
     * 
     * ※BlancoCgInterface展開の際に、このメソッドを共通処理として呼び出してはなりません。
     * その共通化は、かえって理解を妨げると判断しています。
     * 
     * @param cgClass
     *            クラスのバリューオブジェクト。
     * @param argBuf
     *            出力先文字列バッファ。
     */
    private void expandExtendClassList(final BlancoCgClass cgClass,
            final BlancoCgSourceFile argSourceFile, final StringBuffer argBuf) {
        for (int index = 0; index < cgClass.getExtendClassList().size(); index++) {
            final Object objType = cgClass.getExtendClassList().get(index);
            if (objType instanceof BlancoCgType == false) {
                throw new IllegalArgumentException("クラスの継承もとにBlancoCgType以外の型["
                        + objType.getClass().getName() + "]が与えられました。");
            }

            final BlancoCgType type = (BlancoCgType) objType;

            // import文に型を追加。
            argSourceFile.getImportList().add(type.getName());

            if (index == 0) {
                argBuf.append("("
                        + BlancoCgTypePythonSourceExpander.toTypeString(type));
            } else {
                argBuf.append(", "
                        + BlancoCgTypePythonSourceExpander.toTypeString(type));
            }

            if (index == cgClass.getExtendClassList().size() - 1) {
                argBuf.append(")");
            }
        }

    }

    /**
     * クラスに含まれる各々のフィールドを展開します。
     * 
     * TODO 定数宣言を優先して展開し、その後変数宣言を展開するなどの工夫が必要です。<br>
     * 現在は 登録順でソースコード展開します。
     * 
     * @param cgClass
     *            処理中のクラス。
     * @param argSourceFile
     *            ソースファイル。
     * @param argSourceLines
     *            ソースコード行リスト。
     */
    private void expandFieldList(final BlancoCgClass cgClass,
            final BlancoCgSourceFile argSourceFile, final List argSourceLines) {
        if (cgClass.getFieldList() == null) {
            // フィールドのリストにnullが与えられました。
            // かならずフィールドのリストにはListをセットしてください。
            throw new IllegalArgumentException("フィールドのリストにnullが与えられました。");
        }

        for (int index = 0; index < cgClass.getFieldList().size(); index++) {
            final Object objField = cgClass.getFieldList().get(index);
            if (objField instanceof BlancoCgField == false) {
                throw new IllegalArgumentException("フィールドのリストにフィールド以外の型["
                        + objField.getClass().getName() + "]の値が与えられました。");
            }
            final BlancoCgField cgField = (BlancoCgField) objField;
            // クラスのフィールドとして展開を行います。
            new BlancoCgFieldPythonSourceExpander().transformField(cgField,
                    argSourceFile, argSourceLines, false);
        }
    }

    /**
     * クラスに含まれる各々のメソッドを展開します。
     * 
     * @param cgClass
     *            処理中のクラス。
     * @param argSourceFile
     *            ソースファイル。
     * @param argSourceLines
     *            ソースコード行リスト。
     */
    private void expandMethodList(final BlancoCgClass cgClass,
            final BlancoCgSourceFile argSourceFile, final List argSourceLines) {
        if (cgClass.getMethodList() == null) {
            throw new IllegalArgumentException("メソッドのリストにnullが与えられました。");
        }
        for (int index = 0; index < cgClass.getMethodList().size(); index++) {
            final Object objMethod = cgClass.getMethodList().get(index);
            if (objMethod instanceof BlancoCgMethod == false) {
                throw new IllegalArgumentException("メソッドのリストにメソッド以外の型["
                        + objMethod.getClass().getName() + "]の値が与えられました。");
            }

            final BlancoCgMethod cgMethod = (BlancoCgMethod) objMethod;
            // クラスのメソッドとして展開を行います。
            new BlancoCgMethodPythonSourceExpander().transformMethod(cgMethod,
                    argSourceFile, argSourceLines, false);
        }
    }
}
