/* vim: set ts=2: */
/**
 * Copyright (c) 2006 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions, and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions, and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *   3. Redistributions must acknowledge that this software was
 *      originally developed by the UCSF Computer Graphics Laboratory
 *      under support by the NIH National Center for Research Resources,
 *      grant P41-RR01081.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package ext.edu.ucsf.rbvi.strucviz2;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;

/**
 * This class provides the implementation for the ChimeraChain object
 * 
 * @author scooter
 * 
 */
// TODO: [Optional] Implement toAttr() method
public class ChimeraChain implements ChimeraStructuralObject
{

  /**
   * The model/subModel number this chain is a part of
   */
  private int modelNumber;

  private int subModelNumber;

  /**
   * A pointer to the model this chain is a part of
   */
  private ChimeraModel chimeraModel;

  /**
   * The chainID (from the PDB record)
   */
  private String chainId;

  /**
   * The residues that are part of this chain
   */
  private TreeMap<String, ChimeraResidue> residueMap;

  /**
   * userData to associate with this chain
   */
  private Object userData;

  /**
   * Flag to indicate the selection state
   */
  private boolean selected = false;

  /**
   * Constructor to create a new ChimeraChain
   * 
   * @param model
   *          the model number this chain is part of
   * @param subModel
   *          the subModel number this chain is part of
   * @param chainId
   *          the chain ID for this chain
   */
  public ChimeraChain(int model, int subModel, String chainId)
  {
    this.modelNumber = model;
    this.subModelNumber = subModel;
    this.chainId = chainId;
    residueMap = new TreeMap<String, ChimeraResidue>();
  }

  /**
   * set the selected state of this chain
   * 
   * @param selected
   *          a boolean to set the selected state to
   */
  public void setSelected(boolean selected)
  {
    this.selected = selected;
  }

  /**
   * return the selected state of this chain
   * 
   * @return the selected state
   */
  public boolean isSelected()
  {
    return selected;
  }

  public boolean hasSelectedChildren()
  {
    if (selected)
    {
      return true;
    }
    else
    {
      for (ChimeraResidue residue : getResidues())
      {
        if (residue.isSelected())
          return true;
      }
    }
    return false;
  }

  /**
   * Return the list of selected residues
   * 
   * @return all selected residues
   */
  public List<ChimeraResidue> getSelectedResidues()
  {
    List<ChimeraResidue> residueList = new ArrayList<ChimeraResidue>();
    if (selected)
    {
      residueList.addAll(getResidues());
    }
    else
    {
      for (ChimeraResidue residue : getResidues())
      {
        if (residue.isSelected())
          residueList.add(residue);
      }
    }
    return residueList;
  }

  /**
   * Add a residue to the chain.
   * 
   * @param residue
   *          the ChimeraResidue to add to the chain.
   */
  public void addResidue(ChimeraResidue residue)
  {
    String index = residue.getIndex();
    // Put it in our map so that we can return it in order
    residueMap.put(index, residue);
  }

  /**
   * Return the list of residues in this chain in pdb residue order
   * 
   * @return a Collection of residues in residue order
   */
  public Collection<ChimeraResidue> getResidues()
  {
    return residueMap.values();
  }

  /**
   * Return the list of residues in this chain as a list
   * 
   * @return List of residues
   */
  public List<ChimeraStructuralObject> getChildren()
  {
    return new ArrayList<ChimeraStructuralObject>(residueMap.values());
  }

  /**
   * Get a specific residue
   * 
   * @param residueIndex
   *          String representation of the residue index
   * @return the ChimeraResidue represented by the residueIndex
   */
  public ChimeraResidue getResidue(String index)
  {
    // Integer index = Integer.valueOf(residueIndex);
    if (residueMap.containsKey(index))
      return residueMap.get(index);
    return null;
  }

  /**
   * Get a list of residues as a residue range
   * 
   * @param residueRange
   *          String representation of the residue range
   * @return the List of ChimeraResidues represented by the range
   */
  public List<ChimeraResidue> getResidueRange(String residueRange)
  {
    String[] range = residueRange.split("-", 2);
    if (range[1] == null || range[1].length() == 0)
    {
      range[1] = range[0];
    }
    List<ChimeraResidue> resultRange = new ArrayList<ChimeraResidue>();
    int start = Integer.parseInt(range[0]);
    int end = Integer.parseInt(range[1]);
    for (int i = start; i <= end; i++)
    {
      String index = String.valueOf(i);
      if (residueMap.containsKey(index))
        resultRange.add(residueMap.get(index));
    }
    return resultRange;
  }

  /**
   * Get the ID for this chain
   * 
   * @return String value of the chainId
   */
  public String getChainId()
  {
    return chainId;
  }

  /**
   * Get the model number for this chain
   * 
   * @return the model number
   */
  public int getModelNumber()
  {
    return modelNumber;
  }

  /**
   * Get the sub-model number for this chain
   * 
   * @return the sub-model number
   */
  public int getSubModelNumber()
  {
    return subModelNumber;
  }

  /**
   * Return a string representation of this chain as follows: Chain
   * <i>chainId</i> (<i>residue_count</i> residues)
   * 
   * @return String representation of chain
   */
  public String displayName()
  {
    if (chainId.equals("_"))
    {
      return ("Chain (no ID) (" + getResidueCount() + " residues)");
    }
    else
    {
      return ("Chain " + chainId + " (" + getResidueCount() + " residues)");
    }
  }

  /**
   * Return a string representation of this chain as follows: Node xxx [Model
   * yyyy Chain <i>chainId</i>]
   * 
   * @return String representation of chain
   */
  public String toString()
  {
    String displayName = chimeraModel.getModelName();
    if (displayName.length() > 14)
      displayName = displayName.substring(0, 13) + "...";
    if (chainId.equals("_"))
    {
      return (displayName + " Chain (no ID) (" + getResidueCount()
              + " residues)");
    }
    else
    {
      return (displayName + " Chain " + chainId + " (" + getResidueCount()
              + " residues)");
    }
  }

  /**
   * Return the Chimera specification for this chain
   * 
   * @return Chimera specification
   */
  public String toSpec()
  {
    if (chainId.equals("_"))
    {
      return ("#" + modelNumber + "." + subModelNumber + ":.");
    }
    else
    {
      return ("#" + modelNumber + "." + subModelNumber + ":." + chainId);
    }
  }

  /**
   * Return the number of residues in this chain
   * 
   * @return integer number of residues
   */
  public int getResidueCount()
  {
    return residueMap.size();
  }

  /**
   * Set the ChimeraModel for this chain
   * 
   * @param model
   *          ChimeraModel to associate with this chain
   */
  public void setChimeraModel(ChimeraModel model)
  {
    this.chimeraModel = model;
  }

  /**
   * Get the ChimeraModel for this chain
   * 
   * @return ChimeraModel associated with this chain
   */
  public ChimeraModel getChimeraModel()
  {
    return chimeraModel;
  }

  /**
   * Get the user data for this Chain
   * 
   * @return user data
   */
  public Object getUserData()
  {
    return userData;
  }

  /**
   * Set the user data for this Chain
   * 
   * @param data
   *          the user data to associate with this chain
   */
  public void setUserData(Object data)
  {
    this.userData = data;
  }
}
