/*
GanttProject is an opensource project management tool.
Copyright (C) 2009-2012 Dmitry Barashev, GanttProject Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.ganttproject.impex.htmlpdf.itext;

import java.awt.BorderLayout;
import java.awt.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.export.ExporterBase;
import net.sourceforge.ganttproject.export.ExportException;
import net.sourceforge.ganttproject.export.ExporterBase.ExporterJob;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.gui.options.OptionsPageBuilder;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.ganttproject.impex.htmlpdf.AbstractEngine;
import org.ganttproject.impex.htmlpdf.ExporterToPDF;
import org.ganttproject.impex.htmlpdf.Stylesheet;
import org.ganttproject.impex.htmlpdf.StylesheetFactoryImpl;
import org.ganttproject.impex.htmlpdf.fonts.TTFontCache;
import org.osgi.service.prefs.Preferences;

import biz.ganttproject.core.option.GPOptionGroup;

public class ITextEngine extends AbstractEngine {
  private ITextStylesheet myStylesheet;
  private final TTFontCache myFontCache;
  private FontSubstitutionModel mySubstitutionModel;
  private Object myFontsMutex = new Object();
  private boolean myFontsReady = false;
  private ExporterToPDF myExporter;

  public ITextEngine(ExporterToPDF exporter) {
    myExporter = exporter;
    myFontCache = new TTFontCache();
    registerFonts();
  }

  public List<GPOptionGroup> getSecondaryOptions() {
    return Arrays.asList(getSecondaryOptionsArray());
  }

  private GPOptionGroup[] getSecondaryOptionsArray() {
    return ((ThemeImpl) myStylesheet).getOptions();
  }

  public Component getCustomOptionsUI() {
    waitRegisterFonts();
    JPanel result = new JPanel(new BorderLayout());
    result.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
    OptionsPageBuilder builder = new OptionsPageBuilder();

    List<GPOptionGroup> options = new ArrayList<GPOptionGroup>();
    options.addAll(myExporter.getSecondaryOptions());
    result.add(builder.buildPlanePage(options.toArray(new GPOptionGroup[0])), BorderLayout.NORTH);
    result.add(createFontPanel(), BorderLayout.CENTER);
    return result;
  }

  public String[] getCommandLineKeys() {
    return new String[] { "itext" };
  }

  private Component createFontPanel() {
    return new FontSubstitutionPanel(mySubstitutionModel).getComponent();
  }

  public void setContext(IGanttProject project, UIFacade uiFacade, Preferences preferences, Stylesheet stylesheet) {
    super.setContext(project, uiFacade, preferences);
    setSelectedStylesheet(stylesheet);
  }

  public void setSelectedStylesheet(Stylesheet stylesheet) {
    waitRegisterFonts();
    myStylesheet = (ITextStylesheet) stylesheet;
    if (getPreferences() != null) {
      Preferences node = getPreferences().node("/configuration/org.ganttproject.impex.htmlpdf/font-substitution");
      mySubstitutionModel = new FontSubstitutionModel(myFontCache, myStylesheet, node);
      myStylesheet.setFontSubstitutionModel(mySubstitutionModel);
    }
  }

  public void setStylesheet(Stylesheet stylesheet) {
    myStylesheet = (ITextStylesheet) stylesheet;
  }

  public List<Stylesheet> getStylesheets() {
    StylesheetFactoryImpl factory = new StylesheetFactoryImpl() {
      @Override
      protected Stylesheet newStylesheet(URL resolvedUrl, String localizedName) {
        return new ThemeImpl(resolvedUrl, localizedName, getExporter(), myFontCache);
      }
    };
    return factory.createStylesheets(ITextStylesheet.class);
  }

  private ExporterBase getExporter() {
    return myExporter;
  }

  private void registerFonts() {
    Thread fontReadingThread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          // Random waiting seems silly, depending on the available
          // resources (CPU speed, number of processes running etc)
          // this might take longer or shorter...
          // FIXME Add some better way of determining whether the fonts can be
          // read already
          Thread.sleep(10000);
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        registerFontDirectories();
        synchronized (ITextEngine.this.myFontsMutex) {
          myFontsReady = true;
          myFontsMutex.notifyAll();
        }
      }
    });
    fontReadingThread.setPriority(Thread.MIN_PRIORITY);
    fontReadingThread.start();
  }

  private void waitRegisterFonts() {
    while (myFontsMutex != null) {
      synchronized (myFontsMutex) {
        if (myFontsReady) {
          break;
        }
        try {
          myFontsMutex.wait();
        } catch (InterruptedException e) {
          GPLogger.log(e);
          break;
        }
      }
    }
  }

  protected void registerFontDirectories() {
    myFontCache.registerDirectory(System.getProperty("java.home") + "/lib/fonts");
    IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
    IConfigurationElement[] configElements = extensionRegistry.getConfigurationElementsFor("org.ganttproject.impex.htmlpdf.FontDirectory");
    for (int i = 0; i < configElements.length; i++) {
      final String dirName = configElements[i].getAttribute("name");
      if (Boolean.TRUE.toString().equalsIgnoreCase(configElements[i].getAttribute("absolute"))) {
        myFontCache.registerDirectory(dirName);
      } else {
        String namespace = configElements[i].getDeclaringExtension().getNamespaceIdentifier();
        URL dirUrl = Platform.getBundle(namespace).getResource(dirName);
        if (dirUrl == null) {
          GPLogger.getLogger(ITextEngine.class).warning(
              "Failed to find directory " + dirName + " in plugin " + namespace);
          continue;
        }
        try {
          URL resolvedDir = Platform.resolve(dirUrl);
          myFontCache.registerDirectory(resolvedDir.getPath());
        } catch (IOException e) {
          GPLogger.getLogger(ITextEngine.class).log(Level.WARNING, e.getMessage(), e);
          continue;
        }
      }
    }
  }

  public ExporterJob[] createJobs(File outputFile, List<File> resultFiles) {
    waitRegisterFonts();
    return new ExporterJob[] { createTransformationJob(outputFile) };
  }

  private ExporterJob createTransformationJob(final File outputFile) {
    ExporterJob result = new ExporterJob("Generating PDF") {
      @Override
      protected IStatus run() {
        assert myStylesheet != null;
        OutputStream out = null;
        try {
          out = new FileOutputStream(outputFile);
          ((ThemeImpl) myStylesheet).run(getProject(), getUiFacade(), out);
        } catch (ExportException e) {
          throw new RuntimeException(e);
        } catch (FileNotFoundException e) {
          throw new RuntimeException(e);
        } finally {
        }
        return Status.OK_STATUS;
      }
    };
    return result;
  }
}
