/*
 * This file is part of the Vamsas Client version 0.2. 
 * Copyright 2010 by Jim Procter, Iain Milne, Pierre Marguerite, 
 *  Andrew Waterhouse and Dominik Lindner.
 * 
 * Earlier versions have also been incorporated into Jalview version 2.4 
 * since 2008, and TOPALi version 2 since 2007.
 * 
 * The Vamsas Client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * The Vamsas Client 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.
 */
package uk.ac.vamsas.client.picking;

import uk.ac.vamsas.objects.core.Input;
import uk.ac.vamsas.objects.core.Pos;
import uk.ac.vamsas.objects.core.RangeType;
import uk.ac.vamsas.objects.core.Seg;

/**
 * Send and receive details about vamsas object selections and selection ranges
 * defined on one or more objects.
 * 
 * @author J.B. Procter
 * 
 */
public class SelectionMessage extends Message {
  private String selectionID;

  private String[] vorbaIDs;

  RangeType ranges;

  private boolean none = false;

  /**
   * parse a message payload as a selection message
   * 
   * @param str
   */
  public SelectionMessage(String str) {
    message = str;
    // Parse message into ...
    String[] elements = str.split("\t");
    String positions[] = null, segs[] = null;
    for (int i = 0; i < elements.length; i++) {
      if (elements[i].startsWith("selectionID="))
        selectionID = elements[i].substring(12);
      if (elements[i].startsWith("vorbaIDs="))
        vorbaIDs = elements[i].substring(9).split("\\|");
      if (elements[i].startsWith("positions=")) {
        positions = elements[i].substring(10).split(",");
      }
      if (elements[i].startsWith("ranges=")) {
        segs = elements[i].substring(7).split(",");
      }
      if (elements[i].equals("none")) {
        none = true;
      }
    }
    if (none) {
      ranges = null;
      vorbaIDs = null;
    }
    if (positions != null) {
      ranges = new Input();
      for (int i = 0; i < positions.length; i++) {
        Pos p = new Pos();
        try {
          p.setI(Integer.parseInt(positions[i]));
          ranges.addPos(p);
        } catch (Exception e) {
          // invalid message - ignore element
        }
      }
    } else if (segs != null) {
      ranges = new Input();
      for (int i = 0; i < segs.length; i += 2) {
        Seg s = new Seg();
        s.setInclusive(segs[i].startsWith("["));
        try {
          s.setStart(Integer.parseInt(segs[i].substring(1)));
          s.setEnd(Integer.parseInt(segs[i + 1]));
          ranges.addSeg(s);
        } catch (Exception e) {
          // invalid message - again ignore element
        }
      }
    }

  }

  /**
   * create a new selection message
   * 
   * @param selectionID
   *          - (may be null) optional handle (or ID) to refer to selection by
   * @param vorbaIDs
   *          - one or more objects to be selected, or null for the empty
   *          selection
   * @param ranges
   *          optional rangetype specifying positions or intervals over
   *          object(s) coordinate system.
   */
  public SelectionMessage(String selectionID, String[] vorbaIDs,
      RangeType ranges) {
    this(selectionID, vorbaIDs, ranges, false);
  }

  public SelectionMessage(String selectionID, String[] vorbaIDs,
      RangeType ranges, boolean none) {
    super();
    this.selectionID = selectionID;
    if (selectionID != null && selectionID.indexOf("\t") > -1) {
      throw new Error(
          "VAMSAS Selection Messages are not allowed to have Tab Characters in their selection ID");
    }
    this.vorbaIDs = vorbaIDs;
    this.ranges = ranges;
    this.none = none;
    StringBuffer message = new StringBuffer();
    message.append("SELECTION\t");
    if (selectionID != null) {
      message.append("selectionID=" + selectionID);
      message.append("\t");
    }
    message.append("vorbaIDs=");
    for (int ids = 0; ids < vorbaIDs.length; ids++) {
      if (ids > 0) {
        message.append("|");
      }
      if (vorbaIDs[ids] == null) {
        throw new Error("null vorbaID in SelectionMessage ID vector.(" + ids
            + ")");
      }
      if (vorbaIDs[ids].indexOf("\t") > -1) {
        throw new Error(
            "Invalid vorbaID string in SelectionMessage ID vector. ("
                + vorbaIDs[ids] + ")");
      }
      message.append(vorbaIDs[ids]);
    }
    if (none) {
      // must have only IDs for the selection or the selection scope - no range
      if (ranges != null) {
        throw new Error("Empty selection cannot specify a range.");
      }
      if ((selectionID == null || selectionID.length() == 0)
          && (vorbaIDs == null || vorbaIDs.length == 0)) {
        throw new Error(
            "Empty selection must have at least a selection ID or at least one vorbaID indicating selection scope.");
      }
      message.append("none\n");
      this.message = message.toString();
      return;
    }
    // Verify that the range has at least one valid vorbaID for it to be defined
    // on.
    if (vorbaIDs == null || vorbaIDs.length == 0) {
      throw new Error(
          "You must specify at least one vorbaID for the selection.");
    }
    if (ranges != null) {
      if (ranges.getPosCount() > 0) {
        message.append("\tpositions=");
        Pos[] pos = ranges.getPos();
        for (int p = 0; p < pos.length; p++) {
          if (p > 0) {
            message.append(",");
          }
          message.append(pos[p].getI());
        }
      } else if (ranges.getSegCount() > 0) {
        message.append("\tranges=");
        Seg[] rng = ranges.getSeg();
        for (int p = 0; p < rng.length; p++) {
          boolean inc = rng[p].getInclusive();
          if (p > 0) {
            message.append(",");
          }
          if (inc) {
            message.append("[");
          } else {
            message.append("(");
          }
          message.append(rng[p].getStart());
          message.append(",");
          message.append(rng[p].getEnd());
        }
      }
    }
    message.append("\n");
    this.message = message.toString();
  }

  /**
   * @return the selectionID
   */
  public String getSelectionID() {
    return selectionID;
  }

  /**
   * @return the vorbaIDs
   */
  public String[] getVorbaIDs() {
    return vorbaIDs;
  }

  /**
   * @return the ranges
   */
  public RangeType getRanges() {
    return ranges;
  }

  /**
   * @return the none
   */
  public boolean isNone() {
    return none;
  }
}
