/*
 * 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.util;

import com.google.common.base.Function;
import com.google.common.base.Supplier;
import junit.framework.Assert;
import org.broad.igv.AbstractHeadlessTest;
import org.broad.igv.Globals;
import org.broad.igv.track.Track;
import org.broad.igv.track.TrackProperties;
import org.broad.igv.track.WindowFunction;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;

import java.awt.*;
import java.util.Calendar;

import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;

/**
 * User: jrobinso
 * Date: Feb 8, 2010
 */
public class ParsingUtilsTest extends AbstractHeadlessTest {

    public final static String characters = "0123456789abcdefghijklmnopqrstuvwxyz";
    public final static int numChars = characters.length();

    private static final int connectTimeout = 30 * 1000;
    @Rule
    public TestRule testTimeout = new Timeout(4 * connectTimeout);

    @Before
    public void setUp() throws Exception {
        super.setUp();
        Globals.CONNECT_TIMEOUT = connectTimeout;
    }

    private String genRandString() {
        int numWords = 10;
        int max_length = 20;
        String ret = "";
        for (int n = 0; n < numWords; n++) {
            ret += getRandWord(max_length) + "\t";
        }
        return ret;
    }

    private String getRandWord(int max_length) {
        int length = (int) Math.random() * max_length + 1;
        String ret = "";
        for (int n = 0; n < length; n++) {
            ret += characters.charAt((int) Math.random() * numChars);
        }
        return ret;
    }

    @Test
    public void testSplit1() {
        String blankColumnLine = "a\tb\t\td";
        String[] tokens = Globals.tabPattern.split(blankColumnLine);
        int nTokens = tokens.length;
        assertEquals(4, nTokens);
        assertEquals("a", tokens[0]);
        assertEquals("b", tokens[1]);
        assertEquals("", tokens[2]);
        assertEquals("d", tokens[3]);
    }

    @Test
    public void testSplit2() {
        String blankColumnLine = "a\tb\t\td\t";
        String[] tokens = Globals.tabPattern.split(blankColumnLine);
        int nTokens = tokens.length;
        assertEquals(4, nTokens);
        assertEquals("a", tokens[0]);
        assertEquals("b", tokens[1]);
        assertEquals("", tokens[2]);
        assertEquals("d", tokens[3]);
    }

    @Test
    public void testSplit3() {
        String blankColumnLine = "\ta\t\tb\t\t\td\t";
        String[] tokens = Globals.tabPattern.split(blankColumnLine);
        int nTokens = tokens.length;
        String[] expTokens = new String[]{"", "a", "", "b", "", "", "d"};
        assertEquals(expTokens.length, nTokens);
        int ii = 0;
        for (String exp : expTokens) {
            Assert.assertEquals(exp, tokens[ii]);
            ii++;
        }
    }
    
    @Test
    public void testGetLastModified_HTTP() throws Exception{
        tstGetLastModified(HttpUtilsTest.broadURLString);
    }


    @Test
    public void testGetLastModified_File() throws Exception{
        tstGetLastModified(TestUtils.DATA_DIR + "bed/test.bed");
    }

    private void tstGetLastModified(String path) {
        long modD = ParsingUtils.getLastModified(path);
        assertTrue(modD > 0);

        //Assuming causality is still intact
        assertTrue(modD < Calendar.getInstance().getTime().getTime());
    }

    @Test
    public void testParseInt() {
        String with_commas = "123456";
        int expected = 123456;
        int actual = ParsingUtils.parseInt(with_commas);
        assertEquals(expected, actual);

        String exp_not = "3.5e4";
        expected = 35000;
        assertEquals(expected, ParsingUtils.parseInt(exp_not));
    }

    @Test
    public void testParseTrackLine() {
        String trackLine = "track type=bigWig name=\"Track 196\" visibility=2 " +
                "description=\" CD34 - H3K27me3 - hg19 - 18.7 M/20.9 M - 61P7DAAXX.6\" " +
                "maxHeightPixels=70 viewLimits=0:18 windowingFunction=mean autoScale=off " +
                "bigDataUrl=http://www.broadinstitute.org/epigenomics/dataportal/track_00196.portal.bw " +
                "color=255,0,0";

        TrackProperties props = new TrackProperties();
        ParsingUtils.parseTrackLine(trackLine, props);
        assertEquals("Track 196", props.getName());
        assertEquals(Track.DisplayMode.EXPANDED, props.getDisplayMode());
        assertEquals(" CD34 - H3K27me3 - hg19 - 18.7 M/20.9 M - 61P7DAAXX.6", props.getDescription());
        assertEquals(70, props.getHeight());
        assertEquals(0, props.getMinValue(), 1.0e-9);
        assertEquals(18, props.getMaxValue(), 1.0e-9);
        assertEquals(WindowFunction.mean, props.getWindowingFunction());
        assertEquals(false, props.isAutoScale());
        assertEquals(new Color(255, 0, 0), props.getColor());
        assertEquals("http://www.broadinstitute.org/epigenomics/dataportal/track_00196.portal.bw", props.getDataURL());
    }


    @Test
    public void testGetIGVExtension() {

        String path = "/foo/bar/mydata.igv";
        assertEquals("igv", ParsingUtils.getIGVExtension(path));

        path = "/foo/bar/mydata.igv.gz";
        assertEquals("igv", ParsingUtils.getIGVExtension(path));

        path = "/foo/bar/mydata.igv.txt";
        assertEquals("igv", ParsingUtils.getIGVExtension(path));

        path = "/foo/bar/mydata.igv.xls";
        assertEquals("igv", ParsingUtils.getIGVExtension(path));

        path = "/foo/bar/mydata.igv.txt.gz";
        assertEquals("igv", ParsingUtils.getIGVExtension(path));

    }

    //@Test
    public void compareSpeedPatternDotSplit() throws Exception {
        int nTrials = 500000;
        TestStringSupplier supplier = new TestStringSupplier(nTrials);

        final char cdelim = '\t';
        final String sdelim = String.valueOf(cdelim);

        Function<String, Void> patternSplitPredicate = new Function<String, Void>() {
            @Override
            public Void apply(String input) {
                String[] tokens = Globals.tabPattern.split(input);
                return null;
            }
        };

        //ParsingUtils.split seems to be about 2x as fast, that is
        //takes 1/2 the time
        Function<String, Void> parsingUtilsPredicate = new Function<String, Void>() {
            String[] buffer = new String[20];

            @Override
            public Void apply(String input) {
                int count = htsjdk.tribble.util.ParsingUtils.split(input, buffer, cdelim);
                return null;
            }
        };

        supplier.reset();
        System.out.println("\nPattern.split");
        TestUtils.timeMethod(supplier, patternSplitPredicate, nTrials);

        supplier.reset();
        System.out.println("\nParsingUtils.split");
        TestUtils.timeMethod(supplier, parsingUtilsPredicate, nTrials);
    }

    //@Test
    public void compareSpeedStringJoin() throws Exception {
        int nTrials = 5000;
        TestStringArraySupplier supplier = new TestStringArraySupplier(nTrials, 100);

        final char cdelim = '\t';
        final String sdelim = String.valueOf(cdelim);

        Function<String[], Void> stringBuilderFunc = new Function<String[], Void>() {
            @Override
            public Void apply(String[] input) {
                StringBuilder stringBuilder = new StringBuilder();
                for (int el = 0; el < input.length; el++) {
                    stringBuilder.append(input[el]);
                    if (el < input.length - 1) {
                        stringBuilder.append(sdelim);
                    }
                }
                return null;
            }
        };

        //ParsingUtils.split seems to be about 2x as fast, that is
        //takes 1/2 the time
        Function<String[], Void> stringAddFunc = new Function<String[], Void>() {
            @Override
            public Void apply(String[] input) {
                String result = "";

                for (int el = 0; el < input.length; el++) {
                    result += input[el];
                    if (el < input.length - 1) {
                        result += sdelim;
                    }
                }

                return null;

                //String res = htsjdk.tribble.util.ParsingUtils.join(sdelim, input);
                //return true;
            }
        };

        supplier.reset();
        System.out.println("\nStringBuilder");
        TestUtils.timeMethod(supplier, stringBuilderFunc, nTrials);

        supplier.reset();
        System.out.println("\nStringAdd");
        TestUtils.timeMethod(supplier, stringAddFunc, nTrials);
    }

    private class TestStringArraySupplier implements Supplier<String[]> {
        final String[][] testStringArrays;
        private int counter = 0;

        TestStringArraySupplier(int nTrials, int maxStringsPerArray) {
            testStringArrays = new String[nTrials][];
            for (int ii = 0; ii < nTrials; ii++) {
                int numEntries = (int) Math.floor(Math.random() * maxStringsPerArray);
                testStringArrays[ii] = genTestStringArray(numEntries);
            }
        }

        @Override
        public String[] get() {
            return testStringArrays[counter++];
        }

        public void reset() {
            counter = 0;
        }
    }

    private class TestStringSupplier implements Supplier<String> {

        final String[] testStrings;
        private int counter = 0;

        TestStringSupplier(int nTrials) {
            testStrings = genTestStringArray(nTrials);
        }


        @Override
        public String get() {
            return testStrings[counter++];
        }

        public void reset() {
            counter = 0;
        }

    }

    private String[] genTestStringArray(int numEntries) {
        String[] testStrings = new String[numEntries];

        //Generate test data
        for (int ii = 0; ii < numEntries; ii++) {
            testStrings[ii] = genRandString();
        }
        return testStrings;
    }


}

