package ics;

import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.util.Calendars;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static java.lang.System.exit;
import static java.lang.System.out;

/**
 * Created by raub_ni on 12/3/14.
 */

public class Main {

  public static void main(String[] args) {
    // TODO Auto-generated method stub
    /**
     * Begin of program
     */

    String outputfilepath = null, htmlpath = null;
    boolean openFileChooser = true;
    List<String> patharr = new ArrayList<String>();

    final long beginTime = System.currentTimeMillis(); // get program start time
    final Logger logger = Logger.getLogger("com.Main"); // create logger
    BasicConfigurator.configure(); // assign basic level for logger
    logger.setLevel(Level.WARN); // assign WARN level for logger

    final Logger logger2 = Logger.getLogger("net.fortuna.ical4j"); // create
                                                                   // logger
    logger2.setLevel(Level.ERROR); // assign TRACE level for logger

    for (String path : args) { // set path to commandline args

      if (path.equalsIgnoreCase("help")) { // check if user need help

        out.println("\nThis program helps you to read an ical file and write "
            + "and sort the different events into \n"
            + "the js file where the calendar reads from.\nHow to use:\n"
            + "To start the program type \n"
            + "java -jar path_to_jar [path to outputfile] [input file] "
            + "[intput file] [more input files]\n"
            + "The outputfile has to be a js-file.\n"
            + "You can take as many input files as you want.\n"
            + "To read this help type java -jar path_to_jar help\n"
            + "If no arguments are provided the program will ask for\n"
            + "out- and input file but then you can only take one input file");
        // print
        // help

        exit(0); // exit program
      }

      if (path.equalsIgnoreCase("-d")) { // check if logger shall debug
        logger.setLevel(Level.DEBUG); // assign DEBUG level for logger
        continue;
      }

      if (path.equalsIgnoreCase("-i")) { // check if logger shall debug
        logger.setLevel(Level.INFO); // assign INFO level for logger
        continue;
      }

      if (path.endsWith(".js")) { // if argument ends with '.js' set
                                  // outputfilepath to path
        outputfilepath = path;
        htmlpath = outputfilepath.replace("layerevents.js", "orbits/");
      } else {
        openFileChooser = false; // file chooser hasn't to be opened
        if (outputfilepath != null) {
          patharr.add(path);
          // add calendar input file to path list patharr
        } else {
          out.println("The argument outputfilepath should be the first "
              + "argument. The outputfile has to be a js-file");
          exit(0);
        }
      }
    }

    if (openFileChooser)
      openFileChooser(patharr, outputfilepath, beginTime, htmlpath);
    // if filechooser has to be opened open it
    new Main(patharr, outputfilepath, beginTime, htmlpath, logger, logger2);
  }

  private static void openFileChooser(List<String> patharr,
      String outputfilepath, long beginTime, String htmlpath) {
    if (outputfilepath == null) {
      JFileChooser outputchooser = new JFileChooser(); // create new
                                                       // JFileChooser
      FileNameExtensionFilter filter = new FileNameExtensionFilter(
          "layerevents", "js");
      outputchooser.setFileFilter(filter);
      outputchooser.setDialogTitle("Choose outputfile"); // set title of
      // filechooser
      outputchooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
      // set mode (which files should be displayed by the filechooser)
      outputchooser.setAcceptAllFileFilterUsed(true);
      // set accept all files in filechooser to true

      if (outputchooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
        // open filechooser and if user clicked 'ok' then...
        File file = outputchooser.getSelectedFile();
        // get file that is selected by the user
        String path;
        path = file.getPath(); // set path to the path of the selected file
        outputfilepath = path; // set outputfilepath to path
      }
    }

    JFileChooser chooser = new JFileChooser(); // create new JFileChooser
    FileNameExtensionFilter filte = new FileNameExtensionFilter("calendar",
        "ics");
    chooser.setFileFilter(filte);
    chooser.setDialogTitle("Choose inputfile"); // set title of filechooser
    chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
    // set mode (which files should be displayed by the filechooser)
    chooser.setAcceptAllFileFilterUsed(true); // set accept all files in
                                              // filechooser to true

    while (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
      // open filechooser and if user clicked 'ok' then...
      File file = chooser.getSelectedFile();
      // get file that is selected by the user
      patharr.add(file.getPath());
      // add calendar input file to path list patharr
    }
  }

  /**
   * Constructor, set logger, addShutdownHook, parse .ics file
   */

  public Main(List<String> patharr, String outputfilepath, final long beginTime,
      String htmlpath, Logger logger, Logger logger2) {

    final Thread mainThread = Thread.currentThread(); // create thread
    Runtime.getRuntime().addShutdownHook(new Thread() {
      // add shutdown hook to runtime so we know when program is ready
      public void run() {
        final long endTime = System.currentTimeMillis();
        // get program end time
        long wholeTime = endTime - beginTime; // calculate runtime
        logger.info("Done in " + wholeTime + " milliseconds!");
        // print runtime
      }
    });
    logger.debug("path: " + patharr + ", outputfilepath: " + outputfilepath
        + ", htmlpath: " + htmlpath);
    for (String path : patharr) {
      parse(path, logger, logger2, outputfilepath, htmlpath);
      // parse and give over path
    }
  }

  /**
   * parses a ical file. calendar is the filename containing the ical data.
   */

  public void parse(String calendar, Logger logger, Logger logger2,
      String outputfilepath, String htmlpath) {
    try {
      Calendar cal = Calendars.load(calendar);

      String layer = null;
      if (calendar.contains("Dump"))
        layer = "Dump"; // if path to ical file contains Dump the layer of the
                        // event is Dump
      else
        layer = "Imaging"; // else the layer is Imaging

      parseEvents(cal, layer, logger, logger2, outputfilepath, htmlpath);
      // parse events and give over calendar and layer
    } catch (java.io.IOException ioe) {
      throw new ICalException("error while loading ics file", ioe);
    } catch (net.fortuna.ical4j.data.ParserException pe) {
      throw new ICalException("Error while parsing ics file", pe);
    }
  }

  private void sortJS(String outputfilepath, Logger logger) {

    ArrayList<String> dates = new ArrayList<String>();

    try {

      File file = new File(outputfilepath); // make file with layerevents.js
      BufferedReader br = new BufferedReader(new FileReader(file));
      // parses "layerevents.js" file
      String s;
      while ((s = br.readLine()) != null) { // while line is not null
        dates.add(s);
      }

        Collections.sort(dates, new Comparator<String>() {
        @Override
        public int compare(String s, String t1) {
          //logger.debug(s +", t: " + t1);
          int sint = Integer.parseInt(s.substring(9, 17));
          // date starts at 9 and ends at 17:
          // AddEvent(20120114, "08:29 Orbit 10243 Amount = 37.234 MB",
          // "Imaging", "", "", "", "", 45, 45, "", "");
          int t1int = Integer.parseInt(t1.substring(9, 17));

          if (sint != t1int) {
            return sint - t1int;
          }

          String time = s.substring(20, 25);
          // time starts at 20 and ends at 25:
          // AddEvent(20120114, "08:29 Orbit 10243 Amount = 37.234 MB",
          // "Imaging", "", "", "", "", 45, 45, "", "");
          String time2 = t1.substring(20, 25);
          return time.compareTo(time2);
        }
      });
      
      ArrayList<String> dat = new ArrayList<String>();
      String last_in = "";
      for (String d : dates) {
        //logger.debug("duplicate check: " + d);
        if (d.equals(last_in)) {
          logger.debug("duplicated: " + d);
        } else {
          dat.add(d);
          last_in = d;
        }
      }
        
      br.close(); // closes the reader
      // create new writer
      PrintWriter pw = new PrintWriter(
          new BufferedWriter(new FileWriter(outputfilepath, false)));
      for (String s2 : dat) { // set s2 to date
        pw.println(s2); // write s2
      }
      pw.close();
    } catch (IOException ioe) {
      ioe.printStackTrace();
    }

  }

  private void parseEvents(Calendar cal, String layer, Logger logger,
      Logger logger2, String outputfilepath, String htmlpath) {

    int lastDate = 0;

    try {
      File file = new File(outputfilepath); // make file with layerevents.js
      if (file.exists()) {
        BufferedReader br = new BufferedReader(new FileReader(file));
        // parses "layerevents.js" file
        String s, ab = "0000000000000000000000000000000000000000";

        while ((s = br.readLine()) != null) { // while line is not null
          if (s.contains(layer)) { // if line contains right layer
            ab = s; // string ab = last line
          }
        }
        ab = ab.substring(9, 17); // extract date of event
        lastDate = Integer.parseInt(ab); // set lastdate to ab that is converted
                                         // in integer
        logger.debug("lastDate in js file: " + ab);
        br.close(); // closes the reader
      }
    } catch (IOException ioe) {
      ioe.printStackTrace();
    }

    writeJS(layer, logger, logger2, outputfilepath, cal, lastDate, htmlpath);
    // open writeJS and give over layer
  }

  private void writeJS(String layer, Logger logger, Logger logger2,
      String outputfilepath, Calendar cal, int lastDate, String htmlpath) {

    VEvent component;
    int counter = 0;

   // for (Iterator<ComponentList> i = cal.getComponents("VEVENT").iterator(); i.hasNext();) {
    for (Iterator i = cal.getComponents("VEVENT").iterator(); i.hasNext();) {
      // for every VEVENT
      component = (VEvent) i.next();

      try {
        String date = component.getStartDate().getValue().substring(0, 15)
            + "+0000"; // get date
        logger.debug("Date: " + date);
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmssZ");
        // set format
        Date d = null;

        try {
          d = format.parse(date);
        } catch (ParseException e) {
          e.printStackTrace();
        }

        format.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        // set timezone to berlin
        String b = format.format(d);
        String c = b.substring(0, 8);
        logger.debug("Berlin date: " + b + ",   day: " + c);

        int currDate = Integer.parseInt(c); // convert currDate into integer
        logger.debug("lastDate: " + lastDate + ", currDate: " + currDate);
        if (currDate >= lastDate) { // check if there is a new event
          // create new writer for "events.js"
          PrintWriter pw = new PrintWriter(
              new BufferedWriter(new FileWriter(outputfilepath, true)));
          String time, hour, min;
          time = b.substring(9, 15); // get date of event
          hour = time.substring(0, 2); // get hour
          min = time.substring(2, 4); // get minute

          String orbitNumFileName = component.getSummary().getValue()
              .substring(6, 11) + ".html"; // get orbitnumber and attach '.html'

          // check if html file of orbitnumber exists. if yes set link to
          // htmlfile, if no set no link

          File file = new File(htmlpath + orbitNumFileName);
          if (file.exists()) {
            pw.println("AddEvent(" + c + ", " + "\"" + hour + ":" + min + "<br>"
                + component.getSummary().getValue() + "\"" + ", " + "\"" + layer
                + "\"" + ", " + "\"\"" + ", " + "\"orbits/" + orbitNumFileName
                + "\"" + ", " + "\"\"" + ", " + "\"\"" + ", \"\", \"\", "
                + "\"\"" + ", " + "\"\"" + ");");
            // writes event with link into "layerevents.js"
          } else {
            // if not exists, write event with link into "layerevents.js"
            pw.println("AddEvent(" + c + ", " + "\"" + hour + ":" + min + "<br>"
                + component.getSummary().getValue() + "\"" + ", " + "\"" + layer
                + "\"" + ", " + "\"\"" + ", " + "\"\"" + ", " + "\"\"" + ", "
                + "\"\"" + ", \"\", \"\", " + "\"\"" + ", " + "\"\"" + ");");
          }
          pw.close(); // closes the writer
        }

        counter++; // counts the written events
        logger.debug(counter + " events done!");

      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
    }
    sortJS(outputfilepath, logger);
  }
}
