/*
 * This code is provided under the terms of GPL version 3.
 * Please see LICENSE file for details
 * (C) Dmitry Barashev, GanttProject team, 2004-2008
 */
package net.sourceforge.ganttproject.chart;

import java.awt.Color;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import net.sourceforge.ganttproject.GanttPreviousStateTask;
import net.sourceforge.ganttproject.chart.item.ChartItem;
import net.sourceforge.ganttproject.chart.item.TaskBoundaryChartItem;
import net.sourceforge.ganttproject.chart.item.TaskNotesChartItem;
import net.sourceforge.ganttproject.chart.item.TaskProgressChartItem;
import net.sourceforge.ganttproject.chart.item.TaskRegularAreaChartItem;
import net.sourceforge.ganttproject.gui.UIConfiguration;
import net.sourceforge.ganttproject.gui.options.OptionsPageBuilder;
import net.sourceforge.ganttproject.gui.options.model.GP1XOptionConverter;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskActivity;
import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade;
import net.sourceforge.ganttproject.task.TaskManager;
import biz.ganttproject.core.chart.canvas.Canvas;
import biz.ganttproject.core.chart.scene.SceneBuilder;
import biz.ganttproject.core.option.ColorOption;
import biz.ganttproject.core.option.DefaultColorOption;
import biz.ganttproject.core.option.EnumerationOption;
import biz.ganttproject.core.option.GPOption;
import biz.ganttproject.core.option.GPOptionGroup;
import biz.ganttproject.core.time.TimeUnitStack;

import com.google.common.collect.Lists;

/**
 * Controls painting of the Gantt chart
 */
public class ChartModelImpl extends ChartModelBase {

  private List<Task> myVisibleTasks;

  private final TaskRendererImpl2 myTaskRendererImpl;

  private TaskManager taskManager;

  // private boolean isPreviousState = false;

  private int rowHeight = 20;

  private final ColorOption myTaskDefaultColorOption;

  private final GPOptionGroup myTaskDefaultsOptions;

  private Set<Task> myHiddenTasks;

  private List<GanttPreviousStateTask> myBaseline;

  public ChartModelImpl(TaskManager taskManager, TimeUnitStack timeUnitStack, final UIConfiguration projectConfig) {
    super(taskManager, timeUnitStack, projectConfig);
    this.taskManager = taskManager;
    myTaskRendererImpl = new TaskRendererImpl2(this);
    getRenderers().add(myTaskRendererImpl);

    class NewTaskColorOption extends DefaultColorOption implements GP1XOptionConverter {
      private NewTaskColorOption() {
        super("taskDefaultColor", new Color(140, 182, 206));
      }

      @Override
      public String getTagName() {
        return "colors";
      }

      @Override
      public String getAttributeName() {
        return "tasks";
      }

      @Override
      public void loadValue(String legacyValue) {
        loadPersistentValue(legacyValue);
        commit();
      }
    }

    myTaskDefaultColorOption = new NewTaskColorOption();
    myTaskDefaultsOptions = new GPOptionGroup("ganttChartDefaults",
        new GPOption[] { taskManager.getTaskNamePrefixOption(), taskManager.getTaskCopyNamePrefixOption(), myTaskDefaultColorOption,
            getTaskManager().getDependencyHardnessOption() });
    myTaskDefaultsOptions.setI18Nkey(
        new OptionsPageBuilder.I18N().getCanonicalOptionLabelKey(getTaskManager().getDependencyHardnessOption()),
        "hardness");
    myTaskDefaultsOptions.setI18Nkey(OptionsPageBuilder.I18N.getCanonicalOptionValueLabelKey("Strong"),
        "hardness.strong");
    myTaskDefaultsOptions.setI18Nkey(OptionsPageBuilder.I18N.getCanonicalOptionValueLabelKey("Rubber"),
        "hardness.rubber");

  }

  @Override
  public void setVisibleTasks(List<Task> visibleTasks) {
    myVisibleTasks = visibleTasks;
  }

  public void setExplicitlyHiddenTasks(Set<Task> hiddenTasks) {
    myHiddenTasks = hiddenTasks;
  }

  @Override
  public ChartItem getChartItemWithCoordinates(int x, int y) {
    ChartItem result = findTaskProgressItem(x, y);
    if (result == null) {
      result = findTaskBoundaryItem(x, y);
    }
    if (result == null) {
      result = super.getChartItemWithCoordinates(x, y);
    }
    return result;
  }

  private ChartItem findTaskProgressItem(int x, int y) {
    ChartItem result = null;
    Canvas.Shape primitive = myTaskRendererImpl.getPrimitiveContainer().getLayer(0).getPrimitive(
        x, 4, y/* - getChartUIConfiguration().getHeaderHeight() */, 0);
    if (primitive instanceof Canvas.Rectangle) {
      Canvas.Rectangle rect = (Canvas.Rectangle) primitive;
      if ("task.progress.end".equals(primitive.getStyle()) && rect.getRightX() >= x - 4 && rect.getRightX() <= x + 4) {
        result = new TaskProgressChartItem((Task) primitive.getModelObject());
      }
    }
    return result;
  }

  public Canvas.Shape getGraphicPrimitive(Object modelObject) {
    for (SceneBuilder renderer : getRenderers()) {
      Canvas.Shape result = renderer.getCanvas().getPrimitive(modelObject);
      if (result != null) {
        return result;
      }
    }
    return null;
  }

  private ChartItem findTaskBoundaryItem(int x, int y) {
    ChartItem result = null;
    Canvas.Shape primitive = myTaskRendererImpl.getPrimitiveContainer().getPrimitive(x, y);
    if (primitive == null) {
      primitive = myTaskRendererImpl.getPrimitiveContainer().getLayer(1).getPrimitive(x, y);
    }
    if (primitive instanceof Canvas.Polygon) {
      Canvas.Polygon rect = (Canvas.Polygon) primitive;
      if ("task.notesMark".equals(rect.getStyle())) {
        return new TaskNotesChartItem((Task)primitive.getModelObject());
      }
      TaskActivity activity = (TaskActivity) primitive.getModelObject();
      if (activity != null) {
        if (activity.isFirst() && rect.getLeftX() - 2 <= x && rect.getLeftX() + 2 >= x) {
          result = new TaskBoundaryChartItem(activity.getOwner(), true);
        }
        if (result == null && activity.isLast() && rect.getRightX() - 2 <= x
            && rect.getRightX() + 2 >= x) {
          result = new TaskBoundaryChartItem(activity.getOwner(), false);
        }
        if (result == null) {
          result = new TaskRegularAreaChartItem(activity.getOwner());
        }
      }
    }
    return result;
  }

  // public java.awt.Rectangle getBoundingRectangle(Task task) {
  // java.awt.Rectangle result = null;
  // TaskActivity[] activities = task.getActivities();
  // for (int i = 0; i < activities.length; i++) {
  // GraphicPrimitiveContainer.Rectangle nextRectangle = myTaskRendererImpl
  // .getPrimitive(activities[i]);
  // if (nextRectangle != null) {
  // java.awt.Rectangle nextAwtRectangle = new java.awt.Rectangle(
  // nextRectangle.myLeftX, nextRectangle.myTopY,
  // nextRectangle.myWidth, nextRectangle.myHeight);
  // if (result == null) {
  // result = nextAwtRectangle;
  // } else {
  // result = result.union(nextAwtRectangle);
  // }
  // }
  // }
  // return result;
  // }

  // GraphicPrimitiveContainer.Rectangle[] getTaskActivityRectangles(Task task)
  // {
  // List<Rectangle> result = new ArrayList<Rectangle>();
  // TaskActivity[] activities = task.getActivities();
  // for (int i = 0; i < activities.length; i++) {
  // GraphicPrimitiveContainer.Rectangle nextRectangle = myTaskRendererImpl
  // .getPrimitive(activities[i]);
  // if (nextRectangle!=null) {
  // result.add(nextRectangle);
  // }
  // }
  // return result.toArray(new GraphicPrimitiveContainer.Rectangle[0]);
  // }

  List<Task> getVisibleTasks() {
    return myVisibleTasks == null ? Collections.<Task> emptyList() : myVisibleTasks;
  }

  TaskContainmentHierarchyFacade getTaskContainment() {
    return myTaskManager.getTaskHierarchy();
  }

  @Override
  public int calculateRowHeight() {
    rowHeight = myTaskRendererImpl.calculateRowHeight();
    return rowHeight;
  }

  // @Override
  // protected int getRowCount() {
  // return getTaskManager().getTaskCount();
  // }

  @Override
  public TaskManager getTaskManager() {
    return taskManager;
  }

  @Override
  public GPOptionGroup[] getChartOptionGroups() {
    GPOptionGroup[] superGroups = super.getChartOptionGroups();
    List<GPOptionGroup> result = Lists.newArrayList();
    result.add(myTaskDefaultsOptions);
    result.addAll(Arrays.asList(superGroups));
    result.add(myTaskRendererImpl.getLabelOptions());
    return result.toArray(new GPOptionGroup[result.size()]);
  }

  public ColorOption getTaskDefaultColorOption() {
    return myTaskDefaultColorOption;
  }

  public GPOptionGroup getTaskLabelOptions() {
    return myTaskRendererImpl.getLabelOptions();
  }

  public int setBaseline(List<GanttPreviousStateTask> tasks) {
    myBaseline = tasks;
    return (calculateRowHeight());
  }

  List<GanttPreviousStateTask> getBaseline() {
    return myBaseline;
  }

  @Override
  public ChartModelBase createCopy() {
    ChartModelImpl result = new ChartModelImpl(getTaskManager(), getTimeUnitStack(), getProjectConfig());
    super.setupCopy(result);
    result.setVisibleTasks(getVisibleTasks());
    result.setBaseline(getBaseline());
    return result;
  }

  public boolean isExplicitlyHidden(Task task) {
    return myHiddenTasks == null ? false : myHiddenTasks.contains(task);
  }

  public EnumerationOption getDependencyHardnessOption() {
    return getTaskManager().getDependencyHardnessOption();
  }
}