/*
 * The MIT License
 *
 * Copyright (c) 2009 The 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 htsjdk.samtools;

import htsjdk.HtsjdkTest;
import htsjdk.samtools.util.CloseableIterator;
import htsjdk.samtools.util.CloserUtil;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class SAMTextReaderTest extends HtsjdkTest {
    private static final String ARRAY_TAG = "xa";

    // Simple input, spot check that parsed correctly, and make sure nothing blows up.
    @Test
    public void testBasic() throws Exception {
        final String seq1 = "AGCTTAGCTAGCTACCTATATCTTGGTCTTGGCCG";
        final String seq2 = "ACCTATATCTTGGCCTTGGCCGATGCGGCCTTGCA";
        final String qual1 = "<<<<<<<<<<<<<<<<<<<<<:<9/,&,22;;<<<";
        final String qual2 = "<<<<<;<<<<7;:<<<6;<<<<<<<<<<<<7<<<<";
        final String fileFormatVersion = "1.0";
        final String sequence = "chr20";
        final int sequenceLength = 62435964;
        final String charTag = "XC";
        final char charValue = 'q';
        final String intTag = "XI";
        final int intValue = 12345;
        final String floatTag = "XF";
        final float floatValue = 1.2345f;
        final String stringTag = "XS";
        final String stringValue = "Hi,Mom!";
        final String samExample = "@HD\tVN:" + fileFormatVersion + "\t" + charTag + ":" + charValue + "\n" +
                "@SQ\tSN:" + sequence + "\tAS:HG18\tLN:" + sequenceLength + "\t" + intTag + ":" + intValue + "\n" +
                "@RG\tID:L1\tPU:SC_1_10\tLB:SC_1\tSM:NA12891" + "\t" + floatTag + ":" + floatValue + "\n" +
                "@RG\tID:L2\tPU:SC_2_12\tLB:SC_2\tSM:NA12891\n" +
                "@PG\tID:0\tVN:1.0\tCL:yo baby\t" + stringTag + ":" + stringValue + "\n" +
                "@PG\tID:2\tVN:1.1\tCL:whassup? ? ? ?\n" +
                "read_28833_29006_6945\t99\tchr20\t28833\t20\t10M1D25M\t=\t28993\t195\t" +
                seq1.toLowerCase() + "\t" + qual1 + "\t" +
                "MF:i:130\tNm:i:1\tH0:i:0\tH1:i:0\tRG:Z:L1\n" +
                "read_28701_28881_323b\t147\tchr20\t28834\t30\t35M\t=\t28701\t-168\t" +
                seq2 + "\t" + qual2 + "\t" +
                "MF:i:18\tNm:i:0\tH0:i:1\tH1:i:0\tRG:Z:L2\n";

        final String[] samResults =
                {"read_28833_29006_6945\t99\tchr20\t28833\t20\t10M1D25M\t=\t28993\t195\t" + seq1 + "\t" + qual1 +
                        "\tH0:i:0\tH1:i:0\tMF:i:130\tRG:Z:L1\tNm:i:1\n",
                        "read_28701_28881_323b\t147\tchr20\t28834\t30\t35M\t=\t28701\t-168\t" + seq2 + "\t" + qual2 +
                                "\tH0:i:1\tH1:i:0\tMF:i:18\tRG:Z:L2\tNm:i:0\n"
                };

        final SamReader samReader = createSamFileReader(samExample);
        final SAMFileHeader fileHeader = samReader.getFileHeader();

        Assert.assertEquals(fileHeader.getVersion(), fileFormatVersion);
        Assert.assertEquals(fileHeader.getAttribute(charTag), Character.toString(charValue));
        final SAMSequenceRecord sequenceRecord = fileHeader.getSequence(sequence);
        Assert.assertNotNull(sequenceRecord);
        Assert.assertEquals(sequenceRecord.getSequenceLength(), sequenceLength);
        Assert.assertEquals(sequenceRecord.getAttribute(intTag), Integer.toString(intValue));
        Assert.assertEquals(fileHeader.getReadGroup("L1").getAttribute(floatTag), Float.toString(floatValue));
        Assert.assertEquals(fileHeader.getProgramRecord("0").getAttribute(stringTag), stringValue);

        final CloseableIterator<SAMRecord> iterator = samReader.iterator();
        int i = 0;
        while (iterator.hasNext()) {
            final SAMRecord rec = iterator.next();
            Assert.assertEquals(rec.getSAMString(), samResults[i++]);
        }
        iterator.close();
        iterator.close();
        samReader.close();
    }

    private SamReader createSamFileReader(final String samExample) {
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(samExample.getBytes());
        return SamReaderFactory.makeDefault().open(SamInputResource.of(inputStream));
    }

    @Test
    public void testUnmapped() {
        final String alignmentFromKris =
                "0\t4\t*\t0\t0\t*\t*\t0\t0\tGCCTCGTAGTGCGCCATCAGTCTATCGATGTCGTTG\t44\"44===;;;;;;;;;::::88844\"4\"\"\"\"\"\"\"\"\n";
        final SamReader samReader = createSamFileReader(alignmentFromKris);
        final CloseableIterator<SAMRecord> iterator = samReader.iterator();
        while (iterator.hasNext()) {
            iterator.next();
        }
        iterator.close();
        CloserUtil.close(samReader);
    }

    /**
     * Colon separates fields of a text tag, but colon is also valid in a tag value, so assert that works properly.
     */
    @Test
    public void testTagWithColon() {
        // Create a SAMRecord with a String tag containing a colon
        final SAMRecordSetBuilder samBuilder = new SAMRecordSetBuilder();
        samBuilder.addUnmappedFragment("Hi,Mom!");
        final SAMRecord rec = samBuilder.iterator().next();
        final String valueWithColons = "A:B::C:::";
        rec.setAttribute(SAMTag.CQ, valueWithColons);
        // Write the record as SAM Text
        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        final SAMFileWriter textWriter = new SAMFileWriterFactory().makeSAMWriter(samBuilder.getHeader(),
                true, os);
        textWriter.addAlignment(rec);
        textWriter.close();

        final SamReader reader = SamReaderFactory.makeDefault().open(SamInputResource.of(new ByteArrayInputStream(os.toByteArray())));
        final SAMRecord recFromText = reader.iterator().next();
        Assert.assertEquals(recFromText.getAttribute(SAMTag.CQ), valueWithColons);
        CloserUtil.close(reader);
    }

    @DataProvider
    public Object[][] getRecordsWithArrays(){
        final String recordBase = "Read\t4\tchr1\t1\t0\t*\t*\t0\t0\tG\t%\t";
        return new Object[][]{
                {recordBase + ARRAY_TAG + ":B:i", new int[0]},
                {recordBase + ARRAY_TAG + ":B:i,", new int[0]},
                {recordBase + ARRAY_TAG + ":B:i,1,2,3,", new int[]{1,2,3}},
        };
    }

    @Test(dataProvider = "getRecordsWithArrays")
    public void testSamRecordCanHandleArrays(String samRecord, Object array){
        final SAMLineParser samLineParser = new SAMLineParser(new SAMFileHeader());
        final SAMRecord record = samLineParser.parseLine(samRecord);
        Assert.assertEquals(record.getAttribute(ARRAY_TAG), array);
    }
}
