/*******************************************************************************
 * Copyright (c) 2009 IGA Tosiki, NTT DATA BUSINESS BRAINS Corp.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    IGA Tosiki (NTT DATA BUSINESS BRAINS Corp.) - initial API and implementation
 *******************************************************************************/
/*
 * blanco Framework
 * Copyright (C) 2009 NTT DATA BUSINESS BRAINS CORPORATION
 * 
 * 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.pdf.page;

import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import blanco.pdf.BlancoPdfWriter;
import blanco.pdf.font.BlancoPdfFontUtil;
import blanco.pdf.font.valueobject.BlancoPdfFontDef;
import blanco.pdf.form.valueobject.BlancoPdfFormLineDef;
import blanco.pdf.form.valueobject.BlancoPdfFormTextDef;
import blanco.pdf.page.valueobject.BlancoPdfItemDef;
import blanco.pdf.page.valueobject.BlancoPdfPageDef;
import blanco.pdf.valueobject.BlancoPdfMetrix;

import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfWriter;

/**
 * PDF ページを書き込むためのクラス。
 * 
 * @author Tosiki IGA
 */
public class BlancoPdfPageWriter {
    protected Map<String, BaseFont> fFontMap = new HashMap<String, BaseFont>();

    protected BlancoPdfPageDef fPageDef = null;
    protected BlancoPdfWriter fWriter = null;

    public BlancoPdfPageWriter(final String confPath,
            final BlancoPdfPageDef pageDef, final OutputStream outStream) {
        fPageDef = pageDef;

        // TODO ライターの作成は、少し後のタイミングが好適?
        // TODO そもそもファイル名は外から与えたい。
        fWriter = new BlancoPdfWriter(pageDef, outStream);
        fWriter.getPdfSetting().setConfPath(confPath);

        drawForm();
    }

    public PdfWriter getWriter() {
        return fWriter.getWriter();
    }

    public void close() {
        try {
            fWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("クローズ時に例外が発生", e);
        }
    }

    /**
     * 帳票設計用のグリッドを描画。
     * 
     * @param writer
     */
    public void drawGridSheet() {
        final BlancoPdfMetrix metrix = new BlancoPdfMetrix();

        metrix.setLineWidth(0.1f);
        fWriter.drawLine(-10, -10, fPageDef.getColumn() * fPageDef.getPitchX(),
                -10, metrix);
        fWriter.drawLine(-10, -10, -10, fPageDef.getRow()
                * fPageDef.getPitchY(), metrix);

        fWriter.drawLine(-10, 0, fPageDef.getColumn() * fPageDef.getPitchX(),
                0, metrix);
        fWriter.drawLine(0, -10, 0, fPageDef.getRow() * fPageDef.getPitchY(),
                metrix);

        for (int x = 0; x <= fPageDef.getColumn(); x++) {
            metrix.setLineWidth(0.1f);
            int length = 3;
            if (x % 5 == 0) {
                length = 5;
            }
            if (x % 10 == 0) {
                length = 2;
                metrix.setLineWidth(1f);
            }
            fWriter.drawLine(x * fPageDef.getPitchX(), -length, x
                    * fPageDef.getPitchX(), 0, metrix);
        }

        for (int y = 0; y <= fPageDef.getRow(); y++) {
            metrix.setLineWidth(0.1f);
            int length = 3;
            if (y % 3 == 0) {
                length = 5;
            }
            if (y % 6 == 0) {
                length = 2;
                metrix.setLineWidth(1f);
            }
            fWriter.drawLine(-length, y * fPageDef.getPitchY(), 0, y
                    * fPageDef.getPitchY(), metrix);
        }

        final BaseFont bf = getFont("gothic");

        // 文字。横方向。
        for (int x = 0; x <= fPageDef.getColumn() / 10; x++) {
            fWriter.drawText(PdfContentByte.ALIGN_LEFT, x
                    * fPageDef.getPitchX() * 10 - fPageDef.getPitchX() / 2, -3,
                    String.valueOf(x), bf, 8, metrix);
        }

        // 文字。縦方向。
        for (int y = 0; y <= fPageDef.getRow() / 6; y++) {
            fWriter.drawText(PdfContentByte.ALIGN_LEFT,
                    -fPageDef.getPitchX() - 2, y * fPageDef.getPitchY() * 6,
                    String.valueOf(y), bf, 8, metrix);
        }

        // 点
        for (int x = 1; x <= fPageDef.getColumn(); x++) {
            for (int y = 1; y <= fPageDef.getRow(); y++) {
                metrix.setColorFill(Color.BLACK);
                fWriter.drawCircle(x * fPageDef.getPitchX(), y
                        * fPageDef.getPitchY(), 0.3f, metrix);
            }
        }

        // 項目を参考として印字。
        for (BlancoPdfItemDef itemDef : fPageDef.getItemList()) {
            final StringBuilder builder = new StringBuilder();
            for (int index = 0; index < itemDef.getLength(); index++) {
                if (itemDef.getDoubleByte() == false) {
                    builder.append("M");
                } else {
                    builder.append("龍");
                }
            }

            metrix.setFont(itemDef.getFont());
            metrix.setFontSize(itemDef.getFontSize());
            drawText(itemDef.getX(), itemDef.getY(), builder.toString(), metrix);
        }
    }

    /**
     * フォームの印字。
     */
    public void drawForm() {
        final BlancoPdfMetrix metrix = new BlancoPdfMetrix();

        metrix.setColorStroke(Color.BLUE);
        metrix.setColorText(Color.BLUE);

        // フォーム腺の印字。
        for (BlancoPdfFormLineDef line : fPageDef.getForm().getLineList()) {
            fWriter.drawLine(line.getStartX(), line.getStartY(),
                    line.getEndX(), line.getEndY(), metrix);
        }

        // フォーム文字の印字。
        for (BlancoPdfFormTextDef text : fPageDef.getForm().getTextList()) {
            metrix.setFont(text.getFont());
            metrix.setFontSize(text.getFontSize());
            drawText(text.getX(), text.getY(), text.getValue(), metrix);
        }
    }

    /**
     * フォントを取得します。
     * 
     * @param fontName
     * @return
     */
    public BaseFont getFont(final String fontName) {
        final BaseFont check = fFontMap.get(fontName);
        if (check == null) {
            final BaseFont bf = BlancoPdfFontUtil.getFont(fWriter
                    .getPdfSetting(), fontName);
            fFontMap.put(fontName, bf);
        }
        return fFontMap.get(fontName);
    }

    /**
     * 指定のグリッド箇所にテキストを出力します。
     * 
     * @param column
     * @param row
     * @param value
     * @param fontName
     */
    public void drawTextGrid(final int column, final int row,
            final String value, final BlancoPdfMetrix metrix) {
        final BlancoPdfFontDef font = BlancoPdfFontUtil.read(fWriter
                .getPdfSetting(), metrix.getFont());
        drawText(column * fPageDef.getPitchX(), row * fPageDef.getPitchY()
                - font.getDescent(), value, metrix);
    }

    /**
     * テキストを描画します。
     * 
     * @param x
     * @param y
     * @param value
     * @param fontSize
     */
    public void drawText(final float x, final float y, final String value,
            final BlancoPdfMetrix metrix) {
        if (value == null) {
            // null だと処理しようがないのであきらめます。
            return;
        }

        float delta = 0;
        for (int index = 0; index < value.length(); index++) {
            final String look = String.valueOf(value.charAt(index));

            boolean isDoubleWidth = false;
            try {
                if (look.getBytes("Windows-31J").length > 1) {
                    isDoubleWidth = true;
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            final BaseFont bf = getFont(metrix.getFont());
            final BlancoPdfFontDef font = BlancoPdfFontUtil.read(fWriter
                    .getPdfSetting(), metrix.getFont());

            float pitchX = metrix.getFontSize();
            if (font.getPitch() > 0) {
                // フォントにピッチ情報があればそれを採用。
                pitchX = font.getPitch();
            }

            fWriter.drawText(PdfContentByte.ALIGN_LEFT, x + delta, y
                    + metrix.getFontSize() - font.getDescent(), look, bf,
                    metrix.getFontSize(), metrix);
            delta += pitchX / 2;
            if (isDoubleWidth) {
                delta += pitchX / 2;
            }
        }
    }

    /**
     * 次ページに進みます。
     */
    public void nextPage() {
        fWriter.newPage();
        // TODO ページの設定を反映したり、template PDF を適用したりする処理が未実装。

        drawForm();
    }
}
