/*
 *                  BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 * 
 * Created on Dec 5, 2007
 * 
 */

package org.biojava.dasobert.feature;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * converts the features from their "raw" representation as a Map into a Feature
 * class
 * 
 * @author Andreas Prlic
 * 
 */
public class FeatureTrackConverter
{

  public static final Color HELIX_COLOR = new Color(255, 51, 51);

  public static final Color STRAND_COLOR = new Color(255, 204, 51);

  public static final Color TURN_COLOR = new Color(204, 204, 204);

  // some annotation types, for which there is a special treatment
  public static final String DISULFID_TYPE = "DISULFID";

  public static final String SECSTRUC_TYPE = "SECSTRUC";

  public static final String METAL_TYPE = "METAL";

  public static final String MSD_SITE_TYPE = "MSD_SITE";

  String type;

  public static final String TYPE_HISTOGRAM = "histogram";

  public static final String TYPE_DEFAULT = "default";

  // rotate between these colors
  public static final Color[] entColors = new Color[]
  { new Color(51, 51, 255), // blue
      new Color(102, 255, 255), // cyan
      new Color(153, 255, 153), // green
      new Color(153, 255, 153), // green
      new Color(255, 153, 153), // pink
      new Color(255, 51, 51), // red
      new Color(255, 51, 255) // pink
  };

  public static final String[] txtColors = new String[]
  { "blue", "pink", "green", "yellow", "red", "cyan", "pink" };

  Map[] stylesheet;

  boolean isHistogram = false;

  public FeatureTrackConverter()
  {
    type = TYPE_DEFAULT;
    stylesheet = new Map[0];
  }

  public FeatureTrackConverter(Map[] stylesheet)
  {

    if (stylesheet == null)
      stylesheet = new Map[0];

    this.stylesheet = stylesheet;

  }

  public FeatureTrackConverter(Map[] stylesheet, boolean isHistogram)
  {
    this(stylesheet);
    this.isHistogram = isHistogram;
    if (isHistogram)
      type = TYPE_HISTOGRAM;
  }

  public String getType()
  {
    return type;
  }

  public void setType(String type)
  {
    if (type.equals(TYPE_HISTOGRAM))
      isHistogram = true;
    this.type = type;
  }

  public boolean isHistogram()
  {
    return isHistogram;
  }

  public void setHistogram(boolean isHistogram)
  {
    this.isHistogram = isHistogram;
  }

  public FeatureTrack[] convertMap2Features(Map[] mapfeatures)
  {
    List features = new ArrayList();

    boolean first = true;
    boolean secstruc = false;
    boolean isGroup = false;

    FeatureTrack feat = null;
    Segment segment = null;

    int featuresCounter = 0;
    String prevGroup = null;

    for (int i = 0; i < mapfeatures.length; i++)
    {

      Map currentFeatureMap = mapfeatures[i];
      String type = (String) currentFeatureMap.get("TYPE");

      String group = (String) currentFeatureMap.get("GROUP");
      if (group != null)
      {
        if (prevGroup != null)
        {
          if (group.equals(prevGroup))
          {
            feat.setName(group);
            isGroup = true;
          }
          else
          {
            isGroup = false;
          }
        }
        else
        {
          isGroup = false;
        }
      }
      else
      {
        isGroup = false;
      }

      // we are skipping literature references for the moment
      // TODO: add a display to spice for non-positional features
      //
      if (type.equals("reference") || type.equals("GOA"))
      {
        continue;
      }

      if (!first)
      {
        // if not first feature

        if ((!secstruc) && (!isGroup))
        {

          // if not secondary structure and not in a group ...
          features = testAddFeatures(features, feat);

        }
        else if (!(type.equals("HELIX") || type.equals("STRAND") || type
                .equals("TURN")))
        {
          // end of secondary structure
          secstruc = false;
          if (feat != null && (!isGroup))
          {
            features = testAddFeatures(features, feat);
          }

        }
      } // end of not first

      first = false;
      if ((!secstruc) && (!isGroup))
      {
        featuresCounter += 1;
        feat = getNewFeat(currentFeatureMap);
      }

      if (type.equals("STRAND"))
      {
        secstruc = true;
        currentFeatureMap.put("colorTxt", "yellow");
        feat.setName("SECSTRUC");
        feat.setType("SECSTRUC");
      }

      else if (type.equals("HELIX"))
      {
        secstruc = true;
        currentFeatureMap.put("colorTxt", "red");
        feat.setName("SECSTRUC");
        feat.setType("SECSTRUC");
      }

      else if (type.equals("TURN"))
      {
        secstruc = true;
        currentFeatureMap.put("colorTxt", "white");

        feat.setName("SECSTRUC");
        feat.setType("SECSTRUC");
      }
      else
      {
        secstruc = false;
        currentFeatureMap.put("colorTxt", txtColors[featuresCounter
                % txtColors.length]);
        if (!isGroup)
        {
          try
          {
            feat.setName(type);

          } catch (NullPointerException e)
          {
            // e.printStackTrace();
            feat.setName("null");
          }
        }
      }

      segment = getNewSegment(currentFeatureMap);

      feat.addSegment(segment);
      prevGroup = group;
    }

    if (feat != null)
      features = testAddFeatures(features, feat);

    return (FeatureTrack[]) features.toArray(new FeatureTrack[features
            .size()]);
  }

  /**
   * test if this features is added as a new feature to the features list, or if
   * it is joint with an already existing one...
   * 
   * @param features
   * @param newFeature
   * @return a List of FeatureTrack objects
   */
  protected List testAddFeatures(List features, FeatureTrack newFeature)
  {

    // System.out.println("testing " + newFeature + " " +
    // newFeature.getScore());
    Iterator iter = features.iterator();

    if (isHistogramFeatureType(newFeature))
    {

      // return histogram type features
      type = TYPE_HISTOGRAM;

      Segment seg = getHistogramSegmentFromFeature(newFeature);

      while (iter.hasNext())
      {
        FeatureTrack knownFeature = (FeatureTrack) iter.next();
        String knownType = knownFeature.getType();

        // System.out.println("found histogram style " + feat);
        // set type of this DAS source to being HISTOGRAM style

        if (knownType.equals(newFeature.getType()))
        {
          // convert the feature into a HistogramSegment and add to the already
          // known feature

          knownFeature.addSegment(seg);
          // we can return now
          return features;
        }

      }
      // we could not link this to any existing feature
      // convert it to a new HistogramFeature
      HistogramFeature hfeat = new HistogramFeature();

      hfeat.setLink(newFeature.getLink());
      hfeat.setMethod(newFeature.getMethod());
      hfeat.setName(newFeature.getName());
      hfeat.setNote(newFeature.getNote());
      hfeat.setScore("0");
      hfeat.setSource(newFeature.getSource());
      hfeat.addSegment(seg);
      hfeat.setType(newFeature.getType());

      newFeature = hfeat;
      features.add(newFeature);
      return features;
    }

    while (iter.hasNext())
    {
      FeatureTrack knownFeature = (FeatureTrack) iter.next();
      // this only compares method source and type ...
      boolean sameFeat = false;
      if (knownFeature.equals(newFeature))
        sameFeat = true;

      if ((knownFeature.getSource().equals(newFeature.getSource()))
              && (knownFeature.getMethod().equals(newFeature.getMethod()))
              && (knownFeature.getNote().equals(newFeature.getNote()))
              && isSecondaryStructureFeat(knownFeature)
              && isSecondaryStructureFeat(newFeature))
        sameFeat = true;

      if (sameFeat)
      {

        // seems to be of same type, method and source, so check if the segments
        // can be joined

        List tmpsegs = knownFeature.getSegments();
        Iterator segiter = tmpsegs.iterator();
        List newsegs = newFeature.getSegments();
        Iterator newsegsiter = newsegs.iterator();
        boolean overlap = false;
        while (newsegsiter.hasNext())
        {
          Segment newseg = (Segment) newsegsiter.next();

          while (segiter.hasNext())
          {
            Segment tmpseg = (Segment) segiter.next();

            if (tmpseg.overlaps(newseg))
              overlap = true;
          }
        }

        if (!overlap)
        {
          // add all new segments to old features...
          newsegsiter = newsegs.iterator();
          while (newsegsiter.hasNext())
          {
            Segment newseg = (Segment) newsegsiter.next();
            knownFeature.addSegment(newseg);
          }

          return features;
        }
      }

    }

    // if we get here, the features could not be joint with any other one, so
    // there is always some overlap
    // add to the list of known features
    features.add(newFeature);
    return features;
  }

  private FeatureTrack getNewFeat(Map currentFeatureMap)
  {
    FeatureTrack feat = new FeatureTrackImpl();
    // logger.finest(currentFeatureMap);
    // System.out.println("DrawableDasSource " + currentFeatureMap);
    feat.setSource((String) currentFeatureMap.get("dassource"));
    feat.setName((String) currentFeatureMap.get("NAME"));
    feat.setType((String) currentFeatureMap.get("TYPE"));
    feat.setLink((String) currentFeatureMap.get("LINK"));
    feat.setNote((String) currentFeatureMap.get("NOTE"));

    String typeID = (String) currentFeatureMap.get("TYPE_ID");
    String typeCategory = (String) currentFeatureMap.get("TYPE_CATEGORY");
    feat.setTypeID(typeID);
    feat.setTypeCategory(typeCategory);

    String method = (String) currentFeatureMap.get("METHOD");
    if (method == null)
    {
      method = "";
    }
    feat.setMethod(method);
    feat.setScore((String) currentFeatureMap.get("SCORE"));
    return feat;
  }

  private Segment getNewSegment(Map featureMap)
  {
    Segment s = new SegmentImpl();
    String sstart = (String) featureMap.get("START");
    String send = (String) featureMap.get("END");
    int start = Integer.parseInt(sstart);
    int end = Integer.parseInt(send);
    s.setStart(start);
    s.setEnd(end);
    s.setName((String) featureMap.get("TYPE"));
    s.setTxtColor((String) featureMap.get("colorTxt"));
    s.setColor((Color) featureMap.get("color"));
    s.setNote((String) featureMap.get("NOTE"));
    return s;

  }

  private boolean isSecondaryStructureFeat(FeatureTrack feat)
  {
    String type = feat.getType();
    if (type.equals("HELIX") || type.equals("STRAND")
            || type.equals("TURN"))
      return true;
    return false;
  }

  private boolean isHistogramFeatureType(FeatureTrack feat)
  {
    String ftype = feat.getType();

    Map[] style = stylesheet;

    // System.out.println("is HistogramFeature type " + ftype + " " + style );

    // todo : move this info into a config file...

    if (ftype.equals("hydrophobicity"))
    {
      return true;
    }
    if (getType().equals(TYPE_HISTOGRAM))
      return true;

    if (style != null)
    {

      for (int i = 0; i < style.length; i++)
      {
        Map m = style[i];

        // make sure the stylesheet is for this feature type
        String styleType = (String) m.get("type");
        if (styleType != null)
        {
          if (!styleType.equals(ftype))
          {
            continue;
          }
        }
        else
        {
          continue;
        }

        String type = (String) m.get("style");
        if (type != null)
        {
          // System.out.println("stylesheet type " + type);
          if (type.equals("gradient") || (type.equals("lineplot"))
                  || (type.equals("histogram")))
          {

            return true;
          }
        }
      }
    }

    return false;
  }

  private HistogramSegment getHistogramSegmentFromFeature(FeatureTrack feat)
  {
    HistogramSegment s = new HistogramSegment();

    double score = 0.0;

    try
    {
      score = Double.parseDouble(feat.getScore());

    } catch (Exception e)
    {
      // e.printStackTrace();
    }
    s.setScore(score);
    List segments = feat.getSegments();
    if (segments.size() > 0)
    {
      Segment seg = (Segment) segments.get(0);
      s.setName(seg.getName());
      s.setStart(seg.getStart());
      s.setEnd(seg.getEnd());
      s.setNote(seg.getNote());
      s.setColor(seg.getColor());
      s.setTxtColor(seg.getTxtColor());
    }

    return s;
  }

}
