/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2015 Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.broad.igv.feature;

import htsjdk.tribble.Feature;
import org.broad.igv.util.Utilities;

import java.util.Objects;


/**
 * Basic class to specify a genomic interval.
 * Coordinates are intended to be 0-based half-open
 * Please do not add or remove any fields (want to keep it very simple).
 * Additional methods for calculating overlap are okay
 *
 * @author jacob
 * @author 2013-May-20
 */
public class Range implements Feature {

    public String chr = null;
    public int start = -1;
    public int end = -1;

    public Range(String chr, int start, int end) {
        this.chr = chr;
        this.start = start;
        this.end = end;
    }


    public String getChr() {
        return chr;
    }

    @Override
    public String getContig() {
        return chr;
    }

    public int getStart() {
        return start;
    }

    public int getEnd() {
        return end;
    }

    public int getLength() {
        return end - start;
    }

    /**
     * Determine whether this interval fully contains the specified
     * input interval.
     * <p>
     * A negative input start position has special meaning.  It is considered within the interval if the interval
     * contains position "0".
     *
     * @param chr
     * @param start
     * @param end
     * @return
     */
    public boolean contains(String chr, int start, int end) {
        return Utilities.objectEqual(this.chr, chr)
                && this.start <= (start < 0 ? 0 : start)
                && this.end >= end;
    }

    /**
     * Determine whether there is any overlap between this interval and the specified interval
     * <p>
     * Negative positions have no special meaning
     *
     * @param chr
     * @param start
     * @param end
     * @return
     */
    public boolean overlaps(String chr, int start, int end) {
        return Utilities.objectEqual(this.chr, chr) && this.start <= end && this.end >= start;
    }

    public boolean overlaps(Range range) {
        return this.overlaps(range.getChr(), range.getStart(), range.getEnd());
    }

    public boolean contains(Range range) {
        return this.contains(range.chr, range.start, range.end);
    }

    public Range union(Range other) {
        if (!chr.equals(other.chr))
            throw new RuntimeException("Incompatible ranges - different chrs (" + chr + " " + other.chr);
        return new Range(chr, Math.min(start, other.start), Math.max(end, other.end));
    }

    public String toString() {
        return this.chr + ":" + this.start + "-" + this.end;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Range range = (Range) o;
        return start == range.start &&
                end == range.end &&
                Objects.equals(chr, range.chr);
    }

    @Override
    public int hashCode() {
        return Objects.hash(chr, start, end);
    }

    public static Range fromString(String str) {

        String[] t1 = str.split(":");
        String[] t2 = t1[1].split("-");
        String chr = t1[0];
        int start = Integer.parseInt(t2[0].replace(",", ""));
        int end = Integer.parseInt(t2[1].replace(",", ""));
        return new Range(chr, start, end);

    }
}
