package com.capgemini.reports.common.scheduling.view;

import com.capgemini.framework.common.StringHelper;
import com.capgemini.framework.common.TeamcenterDateFormatter;
import com.capgemini.reports.database.HibernateUtil;
import com.capgemini.reports.database.javabean.enumeration.SchedulingEnum;
import com.capgemini.reports.database.manager.MngSchedulingCriteriaVal;
import com.capgemini.reports.periodicity.*;
import com.capgemini.reports.periodicity.SchedulingPeriod.PeriodVisitor;
import com.hp.gagawa.java.elements.*;
import lombok.Getter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import java.text.SimpleDateFormat;

/**
 * Formatting class for schedulings. Can extract scheduling data from the DB, and format it into html {@link Div}s and {@link Input}s.<br>
 * Implements the {@link PeriodVisitor} interface, to analyze a {@link SchedulingPeriod} and show it as a html {@link Div}.
 */
@Getter
public class SchedulingFormatter implements PeriodVisitor<Div> {

  // Period keys
  private static final String PERIOD_DAY_IN_MONTH_VALUE = SchedulingEnum.DAY_IN_MONTH_VALUE.getCriteriaName();
  private static final String PERIOD_WEEKEND = SchedulingEnum.WEEKEND.getCriteriaName();
  private static final String PERIOD_WEEKLY = SchedulingEnum.WEEKLY.getCriteriaName();

  // Period Values
  private static final String KEY_DAY_IN_MONTH_VALUE = PERIOD_DAY_IN_MONTH_VALUE; // This one is both a Periodicity value and a key
  private static final String KEY_END_OF_MONTH = WeekendPeriod.END_MONTH.getStrMonthType();
  private static final String KEY_FIRST_WE = WeekendPeriod.FIRST_WEEK_END.getStrMonthType();
  private static final String KEY_SECOND_WE = WeekendPeriod.SECOND_WEEK_END.getStrMonthType();
  private static final String KEY_THIRD_WE = WeekendPeriod.THIRD_WEEK_END.getStrMonthType();
  private static final String KEY_FOURTH_WE = WeekendPeriod.FOURTH_WEEK_END.getStrMonthType();
  private static final String KEY_FIFTH_WE = WeekendPeriod.FIFTH_WEEK_END.getStrMonthType();

  // Error message
  private static final String MESSAGE_REPORT_NOT_AUTHORIZED = "Ce rapport n'est pas disponible, car il est planifié";

  // html Positioning
  protected static final String MARGIN_TOP_10PX = " margin-top: 10px; ";
  private static final String MARGIN_BOTTOM_20PX = " margin-bottom: 10px; ";
  private static final String P_STYLE_SUBPERIOD = "display:inline;margin-right:10px; ";
  private static final String DATEPICKER_KEY = "datepicker_planification";

  // html constants
  private static final String TYPE_TEXT = "text";
  private static final String TYPE_RADIO = "radio";
  private static final String EVENT_ON_CLICK = "onclick";
  private static final String CHECKED = "checked";

  // CSS constants
  private static final String CSS_CLASS_RADIO_INPUT = "radioInput";

  // Translation to french
  private static final String TEXT_ALL_DAYS = "Tous les jours";
  private static final String TEXT_ALL_WEEK = "Toutes les semaines";
  private static final String TEXT_ALL_MONTH = "Tous les mois";
  private static final String TEXT_BY_DATE = "Date";

  // Weekdays in french
  private static final String MONDAY_FR = "Lundi";
  private static final String TUESDAY_FR = "Mardi";
  private static final String WEDNESDAY_FR = "Mercredi";
  private static final String THURSDAY_FR = "Jeudi";
  private static final String FRIDAY_FR = "Vendredi";

  // Weekday Nums
  private static final int MONDAY_NUM = 1;
  private static final int TUESDAY_NUM = 2;
  private static final int WEDNESDAY_NUM = 3;
  private static final int THURSDAY_NUM = 4;
  private static final int FRIDAY_NUM = 5;

  // Weekends in french
  private static final String FR_DAY_IN_MONTH = "Jour dans le mois";
  private static final String FR_END_OF_MONTH = "Fin du mois";
  private static final String FR_FIRST_WE = "1<sup>er</sup> Week-End";
  private static final String FR_SECOND_WE = "2<sup>nd</sup> Week-End";
  private static final String FR_THIRD_WE = "3<sup>&egrave;me</sup> Week-End";
  private static final String FR_FOURTH_WE = "4<sup>&egrave;me</sup> Week-End";
  private static final String FR_FIFTH_WE = "5<sup>&egrave;me</sup> Week-End";

  // Javascript functions
  protected static final String SUB_TITLE_PLANIFICATION = "subTitle_planification";
  private static final String SUB_TITLE_PLANIFICATION_REQUIRED = "subTitle_planification_Required";
  protected static final String SUB_TITLE_SUB_PERIOD = "subTitle_sub_period";

  /* */
  Div getPeriod(final SchedulingEnum thePeriod, final Long identifier) {
    // TEXT period
    P paragraph = new P();
    paragraph.setStyle("margin-left: 20px;  display:inline;  margin-right:30px;").setCSSClass(SUB_TITLE_PLANIFICATION_REQUIRED)
      .appendText("P&eacute;riode* : ");
    // period
    Div period = makePeriod(thePeriod, identifier);
    return new Div().setStyle(MARGIN_TOP_10PX).setAlign("left").appendChild(paragraph).appendChild(period);
  }


  /*Make bloc period */
  private Div makePeriod(final SchedulingEnum thePeriod, final Long identifier) {
    String name = "period";

    Div containerPeriod = new Div();
    containerPeriod.setStyle("margin-left: 80px; margin-top: 10px;display:inline;").setId("div_period");
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.openSession();
    try {
      String activePeriod = identifier == null ? "" : new MngSchedulingCriteriaVal().getPeriodOfSchedulingReportById(session, identifier).getName();

      // Construct all the Radio Buttons
      boolean isDaily = thePeriod == SchedulingEnum.DAILY;
      boolean isWeekly = thePeriod == SchedulingEnum.WEEKLY;
      boolean isMonthly = thePeriod == SchedulingEnum.WEEKEND || thePeriod == SchedulingEnum.DAY_IN_MONTH_VALUE;
      boolean isDate = thePeriod == SchedulingEnum.DATE;

      Input daily = inputRadio(name, SchedulingEnum.DAILY, isDaily, EVENT_ON_CLICK,
        makeJsFunctionGetSubPeriod(SchedulingEnum.DAILY, identifier, activePeriod));

      Input weekly = inputRadio(name, SchedulingEnum.WEEKLY, isWeekly, EVENT_ON_CLICK,
        makeJsFunctionGetSubPeriod(SchedulingEnum.WEEKLY, identifier, activePeriod));

      Input monthly = inputRadio(name, SchedulingEnum.WEEKEND, isMonthly, EVENT_ON_CLICK,
        makeJsFunctionGetSubPeriod(SchedulingEnum.WEEKEND, identifier, activePeriod)).setId("div_period_monthly");

      Input date = inputRadio(name, SchedulingEnum.DATE, isDate, EVENT_ON_CLICK,
        makeJsFunctionGetSubPeriod(SchedulingEnum.DATE, identifier, activePeriod)).setId("div_period_date");

      // Construct the Div orderly
      containerPeriod.appendChild(daily).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(TEXT_ALL_DAYS));
      containerPeriod.appendChild(weekly).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(TEXT_ALL_WEEK));
      containerPeriod.appendChild(monthly).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(TEXT_ALL_MONTH));
      containerPeriod.appendChild(date).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(TEXT_BY_DATE));

      return containerPeriod;
    }
    finally {
      session.close();
    }
  }

  /* Function for period */
  private String makeJsFunctionGetSubPeriod(final SchedulingEnum period, final Long identifier, final String activePeriod) {
    return "getSubPeriod('" + period.toString() + "', '" + identifier + "', '" + period.getCriteriaName().equals(activePeriod) + "');";
  }

  Div getSchedulingName(final String theScheduligName) {
    String schedulingName = "scheduling_name";
    Label labelSchedulingName = makeLabel(schedulingName, "margin-left: 20px; margin-right: 140px;", "Nom* :")
      .setCSSClass(SUB_TITLE_PLANIFICATION + " " + SUB_TITLE_PLANIFICATION_REQUIRED);
    Input inputSchedulingName = inputText(schedulingName, theScheduligName).setStyle("width:380px;height:20px;").setId(schedulingName);
    return new Div().setId("div_scheduling_name").setAlign("left").appendChild(labelSchedulingName).appendChild(inputSchedulingName);
  }

  private Label makeLabel(final String forName, final String style, final String text) {
    return new Label().setFor(forName).setStyle(style).appendChild(new Text(text));
  }

  /**
   * Creates an {@link Input} of type Radio Button.
   */
  private static Input inputRadio(final String name, final String value, final boolean checked) {
    Input radio = new Input();
    radio.setCSSClass(CSS_CLASS_RADIO_INPUT).setName(name).setType(TYPE_RADIO).setValue(value);
    if (checked) {
      radio.setAttribute(CHECKED, CHECKED);
    }
    return radio;
  }

  /**
   * Creates an {@link Input} of type Radio Button.<br>
   * Example: if checked is <code>true</code> and event in <code>onclick</code><br>
   * &lt;input class="radioInput" type="radio" name=${NAME} value=${VALUE} onclick=${FUNCTION}&gt;
   */
  private static Input inputRadio(final String name, final SchedulingEnum value, final boolean checked, final String typeEvent,
                                  final String functionOnEvent) {
    Input radio = new Input();
    radio.setCSSClass(CSS_CLASS_RADIO_INPUT).setType(TYPE_RADIO).setName(name).setValue(value.getCriteriaName()).setAttribute(typeEvent,
      functionOnEvent);
    if (checked) {
      radio.setAttribute(CHECKED, CHECKED);
    }
    return radio;
  }

  /**
   * Creates an {@link Input} of type "text".<br>
   * Example:<br>
   * &lt;input type="text" name=${NAME} value=${VALUE}&gt;
   *
   * @param name  the name of the input
   * @param value the value inside the input
   * @return a text {@link Input}
   */
  private static Input inputText(final String name, final String value) {
    return new Input().setType(TYPE_TEXT).setName(name).setValue(value);
  }

  @Override
  public Div visit(final DayInMonth theDayInMonth) {
    // Build the 'day_in_month' radio and input.
    Input dayInMonth = inputRadio(PERIOD_WEEKEND, KEY_DAY_IN_MONTH_VALUE, true); // True because this is DAY_IN_MONTH period
    Input dayInMonthValue = inputText(PERIOD_DAY_IN_MONTH_VALUE, theDayInMonth.getDayInMonth().toString()).setMaxlength("2")
      .setId("dayInMonthValue").setStyle("width:24px;height:20px;margin-right:10px;");

    // Even if DayInMonth is selected, each weekend button must also be constructed (unchecked), as both are accessible via the same radio button

    Input endOfMonth = inputRadio(PERIOD_WEEKEND, KEY_END_OF_MONTH, false); // False because this is DAY_IN_MONTH PERIOD
    Input firstWE = inputRadio(PERIOD_WEEKEND, KEY_FIRST_WE, false); // False because this is DAY_IN_MONTH PERIOD
    Input secondWE = inputRadio(PERIOD_WEEKEND, KEY_SECOND_WE, false); // False because this is DAY_IN_MONTH PERIOD
    Input thirdWE = inputRadio(PERIOD_WEEKEND, KEY_THIRD_WE, false); // False because this is DAY_IN_MONTH PERIOD
    Input fourthWE = inputRadio(PERIOD_WEEKEND, KEY_FOURTH_WE, false); // False because this is DAY_IN_MONTH PERIOD
    Input fifthWE = inputRadio(PERIOD_WEEKEND, KEY_FIFTH_WE, false); // False because this is DAY_IN_MONTH PERIOD


    // Make a Div and (orderly) add the radio buttons to it
    Div containerWeekEnd = new Div();
    containerWeekEnd.setStyle("margin-left: 18px; display:inline;").setId("div_weekend");
    containerWeekEnd.appendChild(dayInMonth).appendChild(new P().setStyle("display:inline; ").appendText(FR_DAY_IN_MONTH));
    containerWeekEnd.appendChild(dayInMonthValue);
    containerWeekEnd.appendChild(endOfMonth).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_END_OF_MONTH));
    containerWeekEnd.appendChild(firstWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_FIRST_WE));
    containerWeekEnd.appendChild(secondWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_SECOND_WE));
    containerWeekEnd.appendChild(thirdWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_THIRD_WE));
    containerWeekEnd.appendChild(fourthWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_FOURTH_WE));
    containerWeekEnd.appendChild(fifthWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_FIFTH_WE));

    return containerWeekEnd;
  }

  @Override
  public Div visit(final WeekendPeriod weekend) {
    // Build the 'day_in_month' radio and input. Even if WeekEnd Period is selected, then day_of_month must also be constructed (blank), as both are accessible via the same radio button
    Input dayInMonth = inputRadio(PERIOD_WEEKEND, KEY_DAY_IN_MONTH_VALUE, false); // False because this is WEEKEND PERIOD
    Input dayInMonthValue = inputText(PERIOD_DAY_IN_MONTH_VALUE, StringHelper.EMPTY).setMaxlength("2").setId("dayInMonthValue")
      .setStyle("width:24px;height:20px;margin-right:10px;"); // Initialize day in month to empty (this is WEEKEND PERIOD)

    // Build the 'weekend' radio buttons. Only the provided weekend will be checked.
    Input endOfMonth = inputRadio(PERIOD_WEEKEND, KEY_END_OF_MONTH, weekend == WeekendPeriod.END_MONTH);
    Input firstWE = inputRadio(PERIOD_WEEKEND, KEY_FIRST_WE, weekend == WeekendPeriod.FIRST_WEEK_END);
    Input secondWE = inputRadio(PERIOD_WEEKEND, KEY_SECOND_WE, weekend == WeekendPeriod.SECOND_WEEK_END);
    Input thirdWE = inputRadio(PERIOD_WEEKEND, KEY_THIRD_WE, weekend == WeekendPeriod.THIRD_WEEK_END);
    Input fourthWE = inputRadio(PERIOD_WEEKEND, KEY_FOURTH_WE, weekend == WeekendPeriod.FOURTH_WEEK_END);
    Input fifthWE = inputRadio(PERIOD_WEEKEND, KEY_FIFTH_WE, weekend == WeekendPeriod.FIFTH_WEEK_END);

    // Make a Div and (orderly) add the radio buttons to it
    Div containerWeekEnd = new Div();
    containerWeekEnd.setStyle("margin-left: 18px; display:inline;").setId("div_weekend");
    containerWeekEnd.appendChild(dayInMonth).appendChild(new P().setStyle("display:inline; ").appendText(FR_DAY_IN_MONTH));
    containerWeekEnd.appendChild(dayInMonthValue);
    containerWeekEnd.appendChild(endOfMonth).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_END_OF_MONTH));
    containerWeekEnd.appendChild(firstWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_FIRST_WE));
    containerWeekEnd.appendChild(secondWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_SECOND_WE));
    containerWeekEnd.appendChild(thirdWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_THIRD_WE));
    containerWeekEnd.appendChild(fourthWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_FOURTH_WE));
    containerWeekEnd.appendChild(fifthWE).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FR_FIFTH_WE));

    return containerWeekEnd;
  }

  @Override
  public Div visit(final SpecificDay specificDay) {
    // TEXT date
    Label labelSchedulingName = makeLabel(DATEPICKER_KEY, "margin-left: 20px; margin-right: 50px; ", "Date de planification");
    String dateStr = new SimpleDateFormat(TeamcenterDateFormatter.DD_MM_YYYY).format(specificDay.getDay());
    Input inputSchedulingName = inputText(DATEPICKER_KEY, dateStr).setId(DATEPICKER_KEY).setStyle("cursor:hand;width:80px;height:20px;");

    Div flowElement = new Div();
    flowElement.setCSSClass("flow-element").appendChild(new Div().setId("container_planification"));
    return new Div().setId("div_date").setStyle(MARGIN_TOP_10PX).appendChild(labelSchedulingName).appendChild(inputSchedulingName)
      .appendChild(flowElement);
  }

  @Override
  public Div visit(final WeeklyPeriod dayInWeek) {
    Input monday = inputRadio(PERIOD_WEEKLY, String.valueOf(MONDAY_NUM), dayInWeek.getDayInWeek() == MONDAY_NUM);
    Input tuesday = inputRadio(PERIOD_WEEKLY, String.valueOf(TUESDAY_NUM), dayInWeek.getDayInWeek() == TUESDAY_NUM);
    Input wednesday = inputRadio(PERIOD_WEEKLY, String.valueOf(WEDNESDAY_NUM), dayInWeek.getDayInWeek() == WEDNESDAY_NUM);
    Input thursday = inputRadio(PERIOD_WEEKLY, String.valueOf(THURSDAY_NUM), dayInWeek.getDayInWeek() == THURSDAY_NUM);
    Input friday = inputRadio(PERIOD_WEEKLY, String.valueOf(FRIDAY_NUM), dayInWeek.getDayInWeek() == FRIDAY_NUM);

    // Put it all (orderly) in a <div> container
    Div containerDaily = new Div();
    containerDaily.setStyle("margin-left: 200px; display:inline;").setId("div_weekly");
    containerDaily.appendChild(monday).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(MONDAY_FR));
    containerDaily.appendChild(tuesday).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(TUESDAY_FR));
    containerDaily.appendChild(wednesday).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(WEDNESDAY_FR));
    containerDaily.appendChild(thursday).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(THURSDAY_FR));
    containerDaily.appendChild(friday).appendChild(new P().setStyle(P_STYLE_SUBPERIOD).appendText(FRIDAY_FR));

    return containerDaily;
  }

  @Override
  public Div visit(final DailyPeriod uselessParam) {
    // TEXT period <p>
    P paragraph = new P();
    paragraph.setStyle("margin-left: 210px;  display:inline;  margin-right:30px;").appendText("Tous les jours");
    // period <div>
    return new Div().setStyle(MARGIN_TOP_10PX).setAlign("left").appendChild(paragraph);
  }

}
