/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.pig.test;

import static org.junit.Assert.assertEquals;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.apache.pig.PigServer;
import org.apache.pig.data.BagFactory;
import org.apache.pig.data.DataBag;
import org.apache.pig.data.Tuple;
import org.apache.pig.parser.ParserException;
import org.apache.pig.test.utils.TestHelper;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Test;

public class TestForEachNestedPlan {

    static MiniGenericCluster cluster = MiniGenericCluster.buildCluster();

    private PigServer pig ;

    public TestForEachNestedPlan() throws Throwable {
        pig = new PigServer(cluster.getExecType(), cluster.getProperties()) ;
    }

    Boolean[] nullFlags = new Boolean[]{ false, true };

    @AfterClass
    public static void oneTimeTearDown() throws Exception {
        cluster.shutDown();
    }

    @Test
    public void testInnerOrderBy() throws Exception {
        for (int i = 0; i < nullFlags.length; i++) {
            System.out.println("Running testInnerOrderBy with nullFlags set to :"
                            + nullFlags[i]);
            File tmpFile = genDataSetFile1(nullFlags[i]);
            pig.registerQuery("a = load '"
                    + Util.generateURI(tmpFile.toString(), pig.getPigContext()) + "'; ");
            pig.registerQuery("b = group a by $0; ");
            pig.registerQuery("c = foreach b { " + "     c1 = order $1 by *; "
                    + "    generate flatten(c1); " + "};");
            Iterator<Tuple> it = pig.openIterator("c");
            Tuple t = null;
            int count = 0;
            while (it.hasNext()) {
                t = it.next();
                System.out.println(count + ":" + t);
                count++;
            }
            Assert.assertEquals(count, 30);
        }
    }

    @Test
    public void testInnerOrderByStarWithSchema() throws Exception {
        File tmpFile = genDataSetFile1(false);
        pig.registerQuery("a = load '" + Util.generateURI(tmpFile.toString(),
                pig.getPigContext()) + "' as (a0, a1);");
        pig.registerQuery("b = group a by a0; ");
        pig.registerQuery("c = foreach b { d = order a by *; "
                + "  generate group, d; };");
        Iterator<Tuple> it = pig.openIterator("c");
        Tuple t = null;
        int count = 0;
        while (it.hasNext()) {
            t = it.next();
            System.out.println(count + ":" + t);
            count++;
        }
        Assert.assertEquals(count, 10);
    }

    @Test
    public void testMultiColInAlias() throws Exception {
    	pig.getPigContext().getProperties().setProperty("pig.exec.nosecondarykey", "true");
    	String INPUT_FILE = "test-multi-alias.txt";
        PrintWriter w = new PrintWriter(new FileWriter(INPUT_FILE));
        w.println("10\tnrai01\t01");
        w.println("20\tnrai02\t02");
        w.close();

        try {

            Util.copyFromLocalToCluster(cluster, INPUT_FILE, INPUT_FILE);
            pig.registerQuery("A = load '" + INPUT_FILE + "' "
                    + "as (a:int, b:chararray, c:int);");
            pig.registerQuery("B = GROUP A BY (a, b);") ;

            DataBag dbfrj = BagFactory.getInstance().newDefaultBag(), dbshj = BagFactory.getInstance().newDefaultBag();
            {
                pig.registerQuery("C = FOREACH B { bg = A.($1,$2); GENERATE group, bg; } ;") ;
                Iterator<Tuple> iter1 = pig.openIterator("C");
                while(iter1.hasNext()) {
                    dbfrj.add(iter1.next());
                }
            }
            {
                pig.registerQuery("D = FOREACH B { GENERATE group, A.($1,$2);};") ;
                Iterator<Tuple> iter2 = pig.openIterator("D");
                while(iter2.hasNext()) {
                    dbshj.add(iter2.next());
                }
            }
            Assert.assertEquals(dbfrj.size(), dbshj.size());
            Assert.assertEquals(true, TestHelper.compareBags(dbfrj, dbshj));

        } finally{
            new File(INPUT_FILE).delete();
            try {
                Util.deleteFile(cluster, INPUT_FILE);
            } catch (IOException e) {
                e.printStackTrace();
                Assert.fail();
            }
        }
    }

    @Test
    public void testAlgebricFuncWithoutGroupBy()
    throws IOException, ParserException {
        String INPUT_FILE = "test-sum.txt";

        PrintWriter w = new PrintWriter(new FileWriter(INPUT_FILE));
        w.println("10\t{(1),(2),(3)}");
        w.println("20\t{(4),(5),(6),(7)}");
        w.println("30\t{(8),(9)}");
        w.close();

        try {

            Util.copyFromLocalToCluster(cluster, INPUT_FILE, INPUT_FILE);

            pig.registerQuery("a = load '" + INPUT_FILE + "' "
                    + "as (id:int, g:bag{t:tuple(u:int)});");
            pig.registerQuery("b = foreach a generate id, SUM(g);") ;

            Iterator<Tuple> iter = pig.openIterator("b");

            List<Tuple> expectedResults =
                Util.getTuplesFromConstantTupleStrings(
                    new String[] {
                            "(10,6L)",
                            "(20,22L)",
                            "(30,17L)"
                    });

            int counter = 0;
            while (iter.hasNext()) {
                assertEquals(expectedResults.get(counter++).toString(),
                        iter.next().toString());
            }

            assertEquals(expectedResults.size(), counter);

        } finally{
            new File(INPUT_FILE).delete();
            try {
                Util.deleteFile(cluster, INPUT_FILE);
            } catch (IOException e) {
                e.printStackTrace();
                Assert.fail();
            }
        }
    }

    @Test
    public void testInnerDistinct()
    throws IOException, ParserException {
        String INPUT_FILE = "test-distinct.txt";

        PrintWriter w = new PrintWriter(new FileWriter(INPUT_FILE));
        w.println("10\t89");
        w.println("20\t78");
        w.println("10\t68");
        w.println("10\t89");
        w.println("20\t92");
        w.close();

        try {
            Util.copyFromLocalToCluster(cluster, INPUT_FILE, INPUT_FILE);

            pig.registerQuery("A = load '" + INPUT_FILE
                    + "' as (age:int, gpa:int);");
            pig.registerQuery("B = group A by age;");
            pig.registerQuery("C = foreach B { D = A.gpa; E = distinct D; " +
            		"generate group, MIN(E); };");

            Iterator<Tuple> iter = pig.openIterator("C");

            List<Tuple> expectedResults =
                Util.getTuplesFromConstantTupleStrings(
                        new String[] {"(10,68)", "(20,78)"});

            int counter = 0;
            while (iter.hasNext()) {
               assertEquals(expectedResults.get(counter++).toString(),
                        iter.next().toString());
            }

            assertEquals(expectedResults.size(), counter);
        } finally{
            new File(INPUT_FILE).delete();
            try {
                Util.deleteFile(cluster, INPUT_FILE);
            } catch (IOException e) {
                e.printStackTrace();
                Assert.fail();
            }
        }
    }

    @Test
    public void testInnerOrderByAliasReuse()
    throws IOException, ParserException {
        String INPUT_FILE = "test-innerorderbyaliasreuse.txt";

        PrintWriter w = new PrintWriter(new FileWriter(INPUT_FILE));
        w.println("1\t4");
        w.println("1\t3");
        w.println("2\t3");
        w.println("2\t4");
        w.close();

        try {
            Util.copyFromLocalToCluster(cluster, INPUT_FILE, INPUT_FILE);

            pig.registerQuery("A = load '" + INPUT_FILE
                    + "' as (v1:int, v2:int);");
            pig.registerQuery("B = group A by v1;");
            pig.registerQuery("C = foreach B { X = A; X = order X by v2 asc; " +
            		"generate flatten(X); };");

            Iterator<Tuple> iter = pig.openIterator("C");

            List<Tuple> expectedResults =
                Util.getTuplesFromConstantTupleStrings(
                        new String[] {"(1,3)", "(1,4)", "(2,3)", "(2,4)"});

            int counter = 0;
            while (iter.hasNext()) {
                assertEquals(expectedResults.get(counter++).toString(),
                        iter.next().toString());
            }

            assertEquals(expectedResults.size(), counter);
        } finally{
            new File(INPUT_FILE).delete();
            try {
                Util.deleteFile(cluster, INPUT_FILE);
            } catch (IOException e) {
                e.printStackTrace();
                Assert.fail();
            }
        }
    }


    /***
     * For generating a sample dataset
     */
    private File genDataSetFile1(boolean withNulls) throws IOException {

        int dataLength = 30;
        String[][] data = new String[dataLength][] ;

        DecimalFormat formatter = new DecimalFormat("0000000");

        Random r = new Random();

        for (int i = 0; i < dataLength; i++) {
            data[i] = new String[2] ;
            // inject nulls randomly
            if(withNulls && r.nextInt(dataLength) < 0.3 * dataLength) {
                data[i][0] = "";
            } else {
                data[i][0] = formatter.format(i % 10);
            }
            data[i][1] = formatter.format((dataLength - i)/2);
        }

        return TestHelper.createTempFile(data) ;
    }
}
