package org.openswing.swing.export.java;

import java.io.*;
import java.lang.reflect.*;
import java.sql.Date;
import java.text.*;
import java.util.*;

import java.awt.*;

import org.openswing.swing.message.receive.java.*;
import org.openswing.swing.message.send.java.*;
import com.lowagie.text.*;
import com.lowagie.text.Font;
import com.lowagie.text.Rectangle;
import com.lowagie.text.rtf.*;


/**
 * <p>Title: OpenSwing Framework</p>
 * <p>Description: This class is used to export grid data in RTF format.</p>
 * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
 *
 * <p> This file is part of OpenSwing Framework.
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the (LGPL) Lesser General Public
 * License as published by the Free Software Foundation;
 *
 *                GNU LESSER GENERAL PUBLIC LICENSE
 *                 Version 2.1, February 1999
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *       The author may be contacted at:
 *           maurocarniel@tin.it</p>
 *
 * @author Mauro Carniel
 * @version 1.0
 */
public class ExportToRTF {


  public ExportToRTF() {
  }


  /**
   * @param opt export options
   * @return Excel document, as byte array
   */
  public byte[] getDocument(ExportOptions opt) throws Throwable {

//    Document document = new Document(new Rectangle(total+30,PageSize.A4.height()));
//    Document document = new Document(new Rectangle(PageSize.A4.width(),PageSize.A4.height()));
    Document document = new Document();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    RtfWriter2 w = RtfWriter2.getInstance(document,baos);
    document.open();


    Object obj = null;
    for(int i=0;i<opt.getComponentsExportOptions().size();i++) {
      obj = opt.getComponentsExportOptions().get(i);
      processComponent(null,0,document,opt,obj);
    }


    document.close();
    return baos.toByteArray();
  }


  private void processComponent(Table table,int parentTableCols,Document document,ExportOptions opt,Object obj) throws Throwable {
    if (obj!=null) {
      GridExportCallbacks callbacks = null;
      if (obj instanceof GridExportOptions) {
        callbacks = (GridExportCallbacks)((GridExportOptions)obj).getCallbacks();
        if (callbacks!=null)
          processComponent(table,parentTableCols,document,opt,callbacks.getHeaderComponent());
        prepareGrid(table,parentTableCols,document,opt,(GridExportOptions)obj);
        if (callbacks!=null)
          processComponent(table,parentTableCols,document,opt,callbacks.getFooterComponent());
      }
      else if (obj instanceof ComponentExportOptions)
        prepareGenericComponent(table,parentTableCols,document,opt,(ComponentExportOptions)obj);
      else
        return;

      if (table!=null) {
        Cell cell = new Cell(new Paragraph("\n"));
//        cell.setColspan(parentTableCols);
        table.addCell(cell);
      }
      else
        document.add(new Paragraph("\n"));
    }
  }


  private void prepareGenericComponent(Table parentTable,int parentTableCols,Document document,ExportOptions exportOptions,ComponentExportOptions opt) throws Throwable {
    if (opt.getCellsContent()==null ||
        opt.getCellsContent().length==0)
      return;

    int cols = opt.getCellsContent()[0].length;
    Object[] row = null;
    Object obj = null;
    SimpleDateFormat sdatf = new SimpleDateFormat(exportOptions.getDateTimeFormat());
    int[] headerwidths = new int[cols];
    for(int i=0;i<headerwidths.length;i++)
      headerwidths[i] = (int)PageSize.A4.width()/cols;

    Table table = new Table(cols);
    table.setWidths(headerwidths);
    table.setBorderWidth(2);
    table.setBorderColor(Color.black);
    table.setGrayFill(exportOptions.getExportToRTFAdapter().getHeaderGrayFill());
    table.setPadding(3);

    for(int i=0;i<opt.getCellsContent().length;i++) {
      row = opt.getCellsContent()[i];
      for(int j=0;j<row.length;j++) {
        obj = row[j];

        if (obj!=null) {
          if (obj instanceof Date ||
                   obj instanceof java.util.Date ||
                   obj instanceof java.sql.Timestamp) {
            table.addCell(new Phrase(sdatf.format((java.util.Date)obj),exportOptions.getExportToRTFAdapter().getGenericComponentFont(i,j,obj)));
          }
          else {
            table.addCell(new Phrase(obj.toString(),exportOptions.getExportToRTFAdapter().getGenericComponentFont(i,j,obj)));
          }
        }
        else {
          table.addCell(new Phrase("",exportOptions.getExportToRTFAdapter().getGenericComponentFont(i,j,null)));
        }

      }
    }

    if (parentTable!=null) {
      Cell c = new Cell(table);
//      c.setColspan(parentTableCols);
      parentTable.addCell(c);
    }
    else
      document.add(table);

  }


  private void prepareGrid(Table parentTable,int parentTableCols,Document document,ExportOptions exportOptions,GridExportOptions opt) throws Throwable {
    // prepare vo getters methods...
    String methodName = null;
    String attributeName = null;
    Hashtable gettersMethods = new Hashtable();
    Method[] voMethods = opt.getValueObjectType().getMethods();
    for(int i=0;i<voMethods.length;i++) {
      methodName = voMethods[i].getName();
      if (methodName.startsWith("get")) {
        attributeName = methodName.substring(3,4).toLowerCase()+methodName.substring(4);
        if (opt.getExportAttrColumns().contains(attributeName))
          gettersMethods.put(attributeName,voMethods[i]);
      }
    }

    Response response = null;
    int start = 0;
    int rownum = 0;
    Object value = null;
    Object vo = null;
    int type;

    SimpleDateFormat sdf = new SimpleDateFormat(exportOptions.getDateFormat());
    SimpleDateFormat sdatf = new SimpleDateFormat(exportOptions.getDateTimeFormat());
    SimpleDateFormat stf = new SimpleDateFormat(exportOptions.getTimeFormat());

    int headerwidths[] = new int[opt.getExportColumns().size()];
    int total = 0;
    for(int i=0;i<opt.getExportColumns().size();i++) {
      headerwidths[i] = Math.max(
          opt.getExportColumns().get(i).toString().length()*10,
          ((Integer)opt.getColumnsWidth().get(opt.getExportAttrColumns().get(i))).intValue()
      );
      total += headerwidths[i];
    }

//    Document document = new Document();
//    ByteArrayOutputStream baos = new ByteArrayOutputStream();
//    RtfWriter2 w = RtfWriter2.getInstance(document,baos);
//    document.open();

    Paragraph line = null;
    if (opt.getTitle()!=null && !opt.getTitle().equals("")) {
      line = new Paragraph(opt.getTitle(),exportOptions.getExportToRTFAdapter().getFontTitle());
      line.setAlignment(Element.ALIGN_CENTER);
      document.add(line);
      document.add(new Paragraph(""));
    }
    String[] filters = opt.getFilteringConditions();
    if (filters!=null) {
      for(int i=0;i<filters.length;i++) {
        line = new Paragraph(filters[i]);
        document.add(line);
      }
      document.add(new Paragraph(""));
    }


    Table table = new Table(opt.getExportColumns().size());
    table.setWidths(headerwidths);
    table.setBorderWidth(2);
    table.setBorderColor(Color.black);
    table.setGrayFill(exportOptions.getExportToRTFAdapter().getHeaderGrayFill());
    table.setPadding(3);

    Phrase cell = null;
    for(int i=0;i<opt.getExportColumns().size();i++) {
      cell = new Phrase(
        opt.getExportColumns().get(i).toString(),
        exportOptions.getExportToRTFAdapter().getHeaderFont(opt.getExportAttrColumns().get(i).toString())
      );
      table.addCell(cell);
    }
//    table.setHeaderRows(1);
//    table.getDefaultCell().setBorderWidth(1);
//    table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
//    table.getDefaultCell().setVerticalAlignment(Element.ALIGN_TOP);


    for(int j=0;j<opt.getTopRows().size();j++) {
      // create a row for each top rows...
      vo = opt.getTopRows().get(j);
      appendRow(
        document,
        exportOptions,
        table,
        vo,
        opt,
        gettersMethods,
        sdf,
        sdatf,
        stf,
        j,
        0
      );
    }


    do {
      response=opt.getGridDataLocator().loadData(
        GridParams.NEXT_BLOCK_ACTION,
        start,
        opt.getFilteredColumns(),
        opt.getCurrentSortedColumns(),
        opt.getCurrentSortedVersusColumns(),
        opt.getValueObjectType(),
        opt.getOtherGridParams()
      );
      if (response.isError())
        throw new Exception(response.getErrorMessage());

      boolean even = false;

      for(int j=0;j<((VOListResponse)response).getRows().size();j++) {
        if (even) {
          even = false;
        } else {
          even = true;
        }

        vo = ((VOListResponse)response).getRows().get(j);

        appendRow(
          document,
          exportOptions,
          table,
          vo,
          opt,
          gettersMethods,
          sdf,
          sdatf,
          stf,
          rownum,
          1
        );

        rownum++;
      }

      start = start+((VOListResponse)response).getRows().size();

      if ( !((VOListResponse)response).isMoreRows() )
        break;
    }
    while (rownum<opt.getMaxRows());


    for(int j=0;j<opt.getBottomRows().size();j++) {
      // create a row for each bottom rows...
      table.setGrayFill(exportOptions.getExportToRTFAdapter().getBottomRowsGrayFill(j));
      vo = opt.getBottomRows().get(j);
      appendRow(
        document,
        exportOptions,
        table,
        vo,
        opt,
        gettersMethods,
        sdf,
        sdatf,
        stf,
        j,
        2
      );
    }


//    rtfTable.importTable(table,total);
    if (parentTable!=null) {
      Cell c = new Cell(table);
//      c.setColspan(parentTableCols);
      parentTable.addCell(c);
//      table.setCellsFitPage(true);
//      parentTable.insertTable(table,new Point(parentTable.getNextRow()+1,0));

    }
    else
      document.add(table);

//    document.add(table);
  }


  /**
   * Append current row to result.
   * @return current row to append
   */
  private void appendRow(
    Document document,
    ExportOptions exportOptions,
    Table table,
    Object vo,
    GridExportOptions opt,
    Hashtable gettersMethods,
    SimpleDateFormat sdf,
    SimpleDateFormat sdatf,
    SimpleDateFormat stf,
    int row,
    int tableType // 0 = header, 1 = body, 2 = footer
  ) throws Throwable {
    int type;
    String aName = null;
    Method getter = null;
    Class clazz = null;
    Object obj = null;

    for(int i=0;i<opt.getExportColumns().size();i++) {
//      value = ((Method)gettersMethods.get(opt.getExportAttrColumns().get(i))).invoke(vo,new Object[0]);

      clazz = vo.getClass();
      obj = vo;
      aName = opt.getExportAttrColumns().get(i).toString();

      // check if the specified attribute is a composed attribute and there exist inner v.o. to instantiate...
      while(aName.indexOf(".")!=-1) {
        try {
          getter = clazz.getMethod(
            "get" +
            aName.substring(0, 1).
            toUpperCase() +
            aName.substring(1,aName.indexOf(".")),
            new Class[0]
          );
        }
        catch (NoSuchMethodException ex2) {
          getter = clazz.getMethod("is"+aName.substring(0,1).toUpperCase()+aName.substring(1,aName.indexOf(".")),new Class[0]);
        }
        aName = aName.substring(aName.indexOf(".")+1);
        clazz = getter.getReturnType();
        obj = getter.invoke(obj,new Object[0]);
        if (obj==null)
          break;
      }

      try {
        getter = clazz.getMethod(
          "get" +
          aName.substring(0, 1).
          toUpperCase() +
          aName.substring(1),
          new Class[0]
        );
      }
      catch (NoSuchMethodException ex2) {
        getter = clazz.getMethod("is"+aName.substring(0,1).toUpperCase()+aName.substring(1),new Class[0]);
      }

      if (obj!=null)
        obj = getter.invoke(obj,new Object[0]);

      if (obj!=null) {
        if (obj instanceof Date ||
                 obj instanceof java.util.Date ||
                 obj instanceof java.sql.Timestamp) {
          type = ((Integer)opt.getColumnsType().get(opt.getExportAttrColumns().get(i))).intValue();
          if (type==opt.TYPE_DATE) {
//                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(new Phrase(sdf.format((java.util.Date)obj),exportOptions.getExportToRTFAdapter().getRowFont(aName)));
          }
          else if (type==opt.TYPE_DATE_TIME) {
//                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(new Phrase(sdatf.format((java.util.Date)obj),exportOptions.getExportToRTFAdapter().getRowFont(aName)));
          }
          else if (type==opt.TYPE_TIME) {
//                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(new Phrase(stf.format((java.util.Date)obj),exportOptions.getExportToRTFAdapter().getRowFont(aName)));
          }
        }
        else {
//              table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
          table.addCell(new Phrase(obj.toString(),exportOptions.getExportToRTFAdapter().getRowFont(aName)));
        }
      }
      else {
        table.addCell(new Phrase("",exportOptions.getExportToRTFAdapter().getRowFont(aName)));
      }
    }

    if (opt.getCallbacks()!=null) {
      if (tableType==0)
        processComponent(
          table,
          opt.getExportColumns().size(),
          document,
          exportOptions,
          opt.getCallbacks().getComponentPerRowInHeader(
            (ValueObject)vo,
            row
          )
        );
      else if (tableType==1)
        processComponent(
          table,
          opt.getExportColumns().size(),
          document,
          exportOptions,
          opt.getCallbacks().getComponentPerRow(
            (ValueObject)vo,
            row
          )
        );
      else if (tableType==2)
        processComponent(
          table,
          opt.getExportColumns().size(),
          document,
          exportOptions,
          opt.getCallbacks().getComponentPerRowInFooter(
            (ValueObject)vo,
            row
          )
        );
    }
  }

}
