package com.brilliance.mda.runtime.mda.util;

import com.brilliance.mda.runtime.mda.*;
import com.brilliance.mda.runtime.mda.driver.MdaDriver;
import com.brilliance.mda.runtime.mda.driver.MdaEnv;
import com.brilliance.mda.runtime.mda.impl.StreamImpl;
import com.brilliance.mda.runtime.request.SelectedParams;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.brilliance.mda.runtime.mda.Constants.CR;

public class Streams {

    private static final Logger log = LoggerFactory.getLogger(Streams.class);

    public static final void copy(InputStream is, OutputStream os)
            throws IOException {
        try {
            IOUtils.copy(is, os);
        } finally {
            MdaUtils.closeQuietly(is);
            MdaUtils.closeQuietly(os);
        }
    }

    public static final void copy(Reader reader, OutputStream os, Charset charset)
            throws IOException {
        try {
            IOUtils.copy(reader, os, charset);
        } finally {
            MdaUtils.closeQuietly(reader);
            MdaUtils.closeQuietly(os);
        }
    }

    //TODO
    public static int streamCount(IStream stream) {
        if (stream == null) return 0;

        return stream.countLines();
    }

    public static int streamSearch(IStream stream, String line) {
        if (stream == null) return 0;
        return stream.searchLine(line);
    }

    public static String streamGetLine(IStream stream, int idx) {
        return stream.getLine(idx);
    }

    public static void streamSet(IStream src, Object dist) {
        if (dist instanceof IStream) {
            streamSetByIStream(src, (IStream) dist);
        } else if (dist instanceof String) {
            streamSetByString(src, (String) dist);
        }
    }

    public static void streamSetByIStream(IStream src, IStream dist) {
        src.clearLines();
        dist.getRows().forEach(src::addLine);
    }

    public static void streamSetByString(IStream src, String str) {
        if (str == null || str.trim().isEmpty()) {
            MdaUtils.clear(src);
            return;
        }
        src.clearLines();
        String[] lines = str.split("[\\n]|\\r\\n");
        Arrays.stream(lines).forEach(line -> src.addLine(line));
    }

    public static void streamSet(IStream src, String str, int len) {
        if (str == null || str.trim().isEmpty()) {
            MdaUtils.clear(src);
            return;
        }
        src.clearLines();
        String[] lines = str.split("[\\n]|\\r\\n");
        Arrays.stream(lines).forEach(line -> {
            int begPos = 0;
            while (begPos + len < line.length()) {
                src.addLine(line.substring(begPos, begPos + len));
                begPos += len;
            }
            if (begPos < line.length()) {
                src.addLine(line.substring(begPos));
            }
        });
    }

    public static void streamSet(IStream src, String str, double len) {
        streamSet(src, str, (int) len);
    }

    public static void streamInsert(IStream stream, int idx, String line) {
        if (stream == null) return;
        stream.insertLine(idx, line);
    }

    public static void streamDelete(IStream stream, int idx) {
        stream.deleteLine(idx);
    }


    public static void streamClear(IStream stream) {
        if (stream != null) {
            //stream.close();
            stream.clearLines();
        }
    }

    public static void getAttributeStream(IStream out, IStream dist, String attr) {
        if (dist == null) {
            return;
        }
        IStream tmp = dist.getAttribute(attr);
        if (tmp == null || tmp.countLines() == 0) {
            out.clearLines();
            return;
        }
        out.setRows(tmp.getRows());
    }

    public static void setAttributeStream(IStream dist, String attr, IStream value) {
        dist.setAttribute(attr, value);
    }

    public static void setSelectedLine(IStream stm, int idx) {
        SelectedParams.setSelectedLine(stm, idx);
    }

    public static int getSelectedLine(IStream stm) {
        return SelectedParams.getSelectedLine(stm);
    }

    public static void setSelectedRow(IModuleList<? extends IModule> list, int index) {
        SelectedParams.setSelectedRow(list, index);
    }

    public static int getSelectedRow(IModuleList<? extends IModule> list) {
        return SelectedParams.getSelectedRow(list);
    }

    public static int getSelectedRowIndex(IModuleList<? extends IModule> list) {
        return getSelectedRow(list);
    }

    public static String streamGetAdditional(IStream stm, String s1, String s2) {
        switch (s1) {
            case "ANNOTATION_TEXT":
                break;
            case "ANNOTATION_CLASS":
                break;
            case "ANNOTATION_HINT":
                break;
            case "ANNOTATION_USER":
                break;
            case "ANNOTATION_TIMESTAMP":
                break;
            case "ANNOTATION_COUNT":
                break;
            case "CHARDATA":
                if (s2.equals("")) {
                    return stm.getValue();
                }
                break;
            case "RANGETEXT":
                break;
            case "SELRANGE":
                break;
        }

        return null;
    }

    public static void streamSetAdditional(IStream stm, String insert, String insPos, String txt) {
    }

    public static boolean streamPrint(IStream logstr) {
        return true;
    }

    public static List<String> streamStrSplit(String str) {
        List<String> lines = new ArrayList<String>();
        if (str == null || str.length() == 0)
            return lines;
        if (str.indexOf("\r\n") == -1)
            lines.add(str);
        else {
            lines.addAll(Arrays.asList(str.split("\r\n")));
            lines.remove(lines.size() - 1);
        }
        return lines;
    }

    //前台方法空实现
    public static String promptStream(IStream stream, String caption, String values) {
        MdaEnv.setGridData(caption, values, stream);
        throw new RulePromptException("Propmt out");
    }

    public static void promptIModule(List<IModule> list) {
        Class<? extends IModule> clazz = list.get(0).getClass();
        List<String> columnList = new ArrayList<>();
        IStream stm1 = new StreamImpl();
        String column = "";
        int i = 1;
        for (Field declaredField : clazz.getDeclaredFields()) {
            column = column + (i - 1) + " " + i + " \"" + declaredField.getName().toUpperCase() + "\"410" + CR;
            columnList.add(declaredField.getName());
            i++;
        }
        for (int index = 0; index < list.size(); index++) {
            IModule iModule1 = list.get(index);
            Class<? extends IModule> clazzIModule = iModule1.getClass();
            StringBuilder stringBuilder = new StringBuilder();
            try {
                for (String columnName : columnList) {
                    String getMethodName = "get" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
                    Method m = clazzIModule.getMethod(getMethodName);
                    String vul = (String) m.invoke(iModule1);
                    stringBuilder.append(vul + "\t");
                }
            } catch (Exception e) {

            }
            Streams.streamInsert(stm1, index, stringBuilder.toString());

        }
        MdaEnv.setGridData(clazz.getSimpleName(), column, stm1);
        throw new RulePromptException("Propmt out");
    }

    public static String promptStream(IStream stgStm, String grdHea, String grdVal, int parseInt, int parseInt1) {
        Integer que = SelectedParams.getNomalSelected();
        if (que == 0) {
            MdaEnv.setGridData(grdHea, grdVal, stgStm);
            throw new RulePromptException("Propmt out");
        }
        return stgStm.getLine(que);
    }


    /**
     * 执行面函面板rule，并将面函模板内容保存至stream
     * TODO 根据i的值实现不同类型的模板转换。
     *
     * @param stream
     * @param panel
     * @param i
     */
    public static void saveTextStream(IStream stream, IPanel panel, int i) {
        MdaEnv.getContext().postRule(panel);
        IDatafield<String> field = MdaDriver.getDatafield(panel.getPath() + "blk");
        if (field != null && Strings.notEmpty(field.getValue())) {
            stream.addLine(field.getValue());
        }
    }

    public static void saveTextStream(IStream stream, IPanel panel) {
    }

    /**
     * 将stream中的面函模板赋值给面板panel
     *
     * @param stm
     * @param panel
     * @param i
     */
    public static void loadTextStream(IStream stm, IPanel panel, int i) {
        String blkName = panel.getName() + "blk";
        IModule model = panel.getParent();
        if (model == null) {
            model = MdaEnv.getContext().getRoot();
        }
        Reflects.fieldSet(model, blkName, stm.getValue());
    }

}
