/*-------------------------------------------------------------------------------------------------
 _______ __   _ _______ _______ ______  ______
 |_____| | \  |    |    |______ |     \ |_____]
 |     | |  \_|    |    ______| |_____/ |_____]

 Copyright (c) 2016, antsdb.com and/or its affiliates. All rights reserved. *-xguo0<@

 This program is free software: you can redistribute it and/or modify it under the terms of the
 GNU GNU Lesser General Public License, version 3, as published by the Free Software Foundation.

 You should have received a copy of the GNU Affero General Public License along with this program.
 If not, see <https://www.gnu.org/licenses/lgpl-3.0.en.html>
-------------------------------------------------------------------------------------------------*/
package com.antsdb.saltedfish.sql.vdm;

import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;

import com.antsdb.saltedfish.cpp.FileBasedHeap;
import com.antsdb.saltedfish.cpp.FishObject;
import com.antsdb.saltedfish.cpp.Heap;
import com.antsdb.saltedfish.cpp.Value;
import com.antsdb.saltedfish.nosql.Humpback;
import com.antsdb.saltedfish.sql.planner.SortKey;
import com.antsdb.saltedfish.util.UberUtil;

public class DumbSorter extends CursorMaker {
    final static Logger _log = UberUtil.getThisLogger();
    
    CursorMaker upstream;
    List<Operator> exprs;
    List<Boolean> sortAsc;

    class MyComparator2 implements Comparator<Item> {
        VdmContext ctx;
        Heap heap;
        Parameters params;
        
        @Override
        public int compare(Item x, Item y) {
            for (int i=0; i<exprs.size(); i++) {
                long px = exprs.get(i).eval(ctx, heap, params, x.pRecord);
                long py = exprs.get(i).eval(ctx, heap, params, y.pRecord);
                int comp = AutoCaster.compare(heap, px, py);
                if (comp != 0) {
                    return sortAsc.get(i) ? comp : -comp;
                }
            }
            return 0;
        }
    }
    
    class MyComparator implements Comparator<Item> {
        @Override
        public int compare(Item x, Item y) {
            for (int i = 0; i < x.key.length; i++) {
                Object xx = x.key[i];
                Object yy = y.key[i];
                int result = UberUtil.safeCompare(xx, yy);
                if (!DumbSorter.this.sortAsc.get(i)) {
                    result = -result;
                }
                if (result != 0) {
                    return result;
                }
            }
            return 0;
        }

        @Override
        public boolean equals(Object obj) {
            return false;
        }
    }

    private static class Item {
        Object[] key;
        long pRecord;
    }
    
    private static class MyCursor extends Cursor {
        List<Item> items;
        int i = 0;
        private FileBasedHeap heap;

        public MyCursor(Humpback humpback, CursorMeta meta) {
            super(meta);
            this.heap = new FileBasedHeap(humpback.geTemp());
        }

        public Heap getHeap() {
            return this.heap;
        }
        
        @Override
        public long next() {
            if (i >= this.items.size()) {
                return 0;
            }
            long pResult = items.get(this.i).pRecord;
            if (pResult != 0) {
                Record.size(pResult);
            }
            this.i++;
            return pResult;
        }

        @Override
        public void close() {
            this.heap.close();
        }
    }
    
    public DumbSorter(CursorMaker upstream, Operator expr, boolean asc, int makerId) {
        this(upstream, Collections.singletonList(expr), Collections.singletonList((Boolean)asc), makerId);
    }
    
    public DumbSorter(CursorMaker upstream, List<Operator> exprs, List<Boolean> sortAsc, int makerId) {
        super();
        this.upstream = upstream;
        this.exprs = exprs;
        this.sortAsc = sortAsc;
        setMakerId(makerId);
    }

    @Override
    public CursorMeta getCursorMeta() {
        return this.upstream.getCursorMeta();
    }

    @Override
    public Object run(VdmContext ctx, Parameters params, long pMaster) {
        AtomicLong counter = ctx.getCursorStats(makerId);
        MyCursor result = null;
        boolean success = false;
        try (Cursor cc = this.upstream.make(ctx, params, pMaster)) {
            result = new MyCursor(ctx.getHumpback(), getCursorMeta());
            Heap heap = result.getHeap();
            List<Item> items = new ArrayList<>();
            for (long pRecord = cc.next(); pRecord != 0; pRecord = cc.next()) {
                if (pRecord != 0) {
                    Record.size(pRecord);
                }
                pRecord = Record.clone(heap, pRecord);
                Item item = new Item();
                item.pRecord = pRecord;
                item.key = getSortKey(ctx, heap, params, pRecord);
                items.add(item);
            }
            counter.addAndGet(items.size());
            MyComparator2 comp = new MyComparator2();
            comp.ctx = ctx;
            comp.heap = heap;
            comp.params = params;
            Collections.sort(items, comp);
            result.items = items;
            success = true;
            return result;
        }
        finally {
            if (!success && (result != null)) {
                _log.warn("unexpected close");
                result.close();
            }
        }
    }

    @Override
    public void explain(int level, List<ExplainRecord> records) {
        ExplainRecord rec = new ExplainRecord(getMakerid(), level, getClass().getSimpleName());
        records.add(rec);
        this.upstream.explain(level+1, records);
    }

    private Object[] getSortKey(VdmContext ctx, Heap heap, Parameters params, long pRecord) {
         Object[] key = new Object[this.exprs.size()];
         int i=0;
         for (Operator expr:this.exprs) {
             long pValue = expr.eval(ctx, heap, params, pRecord);
             Object value = FishObject.get(heap, pValue);
             if ((pValue != 0) && (value == null)) {
                 byte format = Value.getFormat(heap, pValue);
                 if (format == Value.FORMAT_DATE) {
                  // mysql '0000-00-00'
                  value = new Date(1);
                 }
                 else if (format == Value.FORMAT_TIMESTAMP) {
               // mysql '0000-00-00 00:00:00'
              value = new Timestamp(1);
                 }
             }
             key[i] = value;
             i++;
         }
        return key;
    }

    @Override
    public boolean setSortingOrder(List<SortKey> order) {
        return false;
    }

    public CursorMaker getUpstream() {
        return this.upstream;
    }

}