package _VisualDVM.TestingSystem.SAPFOR.Json;
import Common.CommonConstants;
import Common.Utils.Utils_;
import _VisualDVM.Constants;
import _VisualDVM.ProjectData.Files.DBProjectFile;
import _VisualDVM.ProjectData.Files.FileType;
import _VisualDVM.ProjectData.Files.ProjectFile;
import _VisualDVM.ProjectData.LanguageName;
import _VisualDVM.ProjectData.Messages.Errors.MessageError;
import _VisualDVM.ProjectData.Project.db_project_info;
import _VisualDVM.Repository.Component.Sapfor.Sapfor;
import _VisualDVM.TestingSystem.SAPFOR.SapforTask.SapforTask;
import _VisualDVM.Utils;
import com.google.gson.annotations.Expose;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Vector;

import static java.lang.Character.isDigit;
public class SapforVersion_json implements Serializable {
    @Expose
    public String version = "";
    @Expose
    public String description = "";
    //поля для отображения деревьев.
    public File Home = null;
    public LinkedHashMap<String, ProjectFile> files = new LinkedHashMap<>();
    //--
    public ProjectFile parse_out = null;
    public ProjectFile parse_err = null;
    public ProjectFile out = null;
    public ProjectFile err = null;
    //--
    public SapforTask task = null; //родная задача. Нужна для построения дерева версий.
    public db_project_info project = null;
    //--
    public SapforVersionState state = null;
    public VersionComparisonState comparisonState = null;
    //--
    public SapforVersion_json(String version_in, String description_in) {
        version = version_in;
        description = description_in;
    }
    public SapforVersion_json(String root_in, String version_in, String description_in) {
        version = version_in.substring(root_in.length() + 1);
        description = description_in;
    }
    public void getFiles(File configurationRoot) {
        //--
        state = SapforVersionState.Empty;
        comparisonState = VersionComparisonState.Unknown;
        //--
        String relativePath = Utils_.isWindows() ? Utils_.toW(version) : version;
        Home = Paths.get(configurationRoot.getAbsolutePath(), relativePath).toFile();
        files = new LinkedHashMap<>();
        //--
        File[] files_ = Home.listFiles();
        if (files_ != null) {
            for (File file : files_) {
                if (file.isFile()) {
                    ProjectFile projectFile = new ProjectFile(file);
                    if (!projectFile.fileType.equals(FileType.forbidden)
                    ) {
                        files.put(projectFile.file.getName(), projectFile);
                    }
                }
            }
            if (!files.isEmpty())
                state = SapforVersionState.Normal;
        }
        parse_out = new ProjectFile(Paths.get(Home.getAbsolutePath(), Constants.data, Constants.parse_out_file).toFile());
        parse_err = new ProjectFile(Paths.get(Home.getAbsolutePath(), Constants.data, Constants.parse_err_file).toFile());
        out = new ProjectFile(Paths.get(Home.getAbsolutePath(), Constants.data, Constants.out_file).toFile());
        err = new ProjectFile(Paths.get(Home.getAbsolutePath(), Constants.data, Constants.err_file).toFile());
        //--
        Vector<File> out_files = new Vector<>();
        out_files.add(parse_out.file);
        out_files.add(parse_err.file);
        out_files.add(out.file);
        out_files.add(err.file);
        for (File file : out_files) {
            try {
                if (file.exists()) {
                    Vector<String> lines = new Vector<>(FileUtils.readLines(file));
                    if (!Sapfor.checkLines(lines)) {
                        state = SapforVersionState.HasErrors;
                        return;
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
    public boolean isMatch(SapforVersion_json version_json) {
        if (!description.equals(version_json.description)) {
            return false;
        }
        if (files.size() != version_json.files.size()) {
            return false;
        }
        for (String name1 : files.keySet()) {
            if (!version_json.files.containsKey(name1)) {
                return false;
            }
        }
        for (String name1 : files.keySet()) {
            ProjectFile file1 = files.get(name1);
            ProjectFile file2 = version_json.files.get(name1);
            //---
            String text1 = "";
            String text2 = "";
            try {
                text1 = FileUtils.readFileToString(file1.file, Charset.defaultCharset());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            try {
                text2 = FileUtils.readFileToString(file2.file, Charset.defaultCharset());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            if (!Utils.compareFortranTexts(text1, text2)) {
                return false;
            }
        }
        return true;
    }
    public MessageError unpackMessage(String line_in) throws Exception {
        MessageError res = new MessageError();
        res.file = "";
        res.line = CommonConstants.Nan;
        res.value = "";
        String line = line_in.substring(9);
        int i = 0;
        int s = 0;
        String lexeme = "";
        //#1020: red43.fdv: line 988]: Active DVM directives are not supported (turn on DVM-directive support option)
        for (char c : line.toCharArray()) {
            switch (s) {
                case 0:
                    //поиск groups_s
                    if (c == '#') {
                        s = 1;
                        lexeme = "";
                    } else return null;
                    break;
                case 1:
                    //group_s
                    if (isDigit(c)) {
                        res.group_s += c;
                        lexeme += c;
                    } else if (c == ':') {
                        s = 2;
                        res.group = Integer.parseInt(lexeme);
                    } else return null;
                    break;
                case 2:
                    //поиск filename
                    if (c == ' ') {
                        s = 3;
                    } else return null;
                    break;
                case 3:
                    //filename
                    if (c == ':') {
                        s = 4;
                    } else {
                        res.file += c;
                    }
                    break;
                case 4:
                    //поиск line
                    if (c == ' ') {
                        s = 5;
                        lexeme = "";
                    } else return null;
                    break;
                case 5:
                    //line
                    if (c == ' ') {
                        if (!lexeme.equals("line"))
                            return null;
                        else {
                            s = 6;
                            lexeme = "";
                        }
                    } else {
                        lexeme += c;
                    }
                    break;
                case 6:
                    //line number
                    if (isDigit(c)) {
                        lexeme += c;
                    } else if (c == ']') {
                        res.line = Integer.parseInt(lexeme);
                        s = 7;
                    } else return null;
                    break;
                case 7:
                    //Поиск value
                    if (c == ':') {
                        s = 8;
                    } else return null;
                    break;
                case 8:
                    if (c == ' ') {
                        s = 9;
                    } else return null;
                    break;
                case 9:
                    //value
                    res.value += c;
                    break;
            }
            ;
            ++i;
        }
        //--
        if (s != 9)
            return null;
        //--
        return res;
    }
    public void readMessagesFromFileDump(File file, Vector<MessageError> messages) {
        try {
            //Образец запакованного сообщения
            //ERROR - [#1020: red43.fdv: line 988]: Active DVM directives are not supported (turn on DVM-directive support option)
            Vector<String> lines = new Vector<>(FileUtils.readLines(file));
            if (!lines.isEmpty()) {
                for (int i = lines.size() - 1; i >= 0; --i) {
                    String line = lines.get(i);
                    if (line.startsWith("ERROR - ")) {
                        MessageError message = unpackMessage(line);
                        if (message != null)
                            messages.add(message);
                        //--
                    } else break;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    //--
    public void createProject(File rootHome) throws Exception {
        project = null;
        String version_ = Utils_.isWindows() ? Utils_.toW(version) : Utils_.toU(version);
        project = new db_project_info();
        project.Home = Paths.get(rootHome.getAbsolutePath(), version_).toFile();
        project.name = project.Home.getName();
        project.description = description;
        project.languageName = LanguageName.fortran;
        project.creationDate = Utils_.getDateNumber();
        //---
        FileUtils.copyDirectory(Home, project.Home);
        ///--------------------------------------
        project.CreateVisualiserData();
    }
    public void ReadMessages() throws Exception {
        if (project != null) {
            Vector<MessageError> messages = new Vector<>();
            //--
            File p_out = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.parse_out_file).toFile();
            File p_err = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.parse_err_file).toFile();
            File out = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.out_file).toFile();
            File err = Paths.get(project.Home.getAbsolutePath(), Constants.data, Constants.err_file).toFile();
            //--
            if (p_out.exists()) {
                project.Log += (FileUtils.readFileToString(p_out));
                readMessagesFromFileDump(p_out, messages);
            }
            if (out.exists()) {
                project.Log += "\n" + FileUtils.readFileToString(out);
                readMessagesFromFileDump(out, messages);
            }
            //в потоки ошибок идет информация от операционной системы. сообщений там быть не должно.
            if (p_err.exists())
                project.Log += (FileUtils.readFileToString(p_err));
            if (err.exists())
                project.Log += "\n" + FileUtils.readFileToString(err);
            //--
            project.Open();
            project.Update(); //Журнал
            //а так же, убрать dep и txt
            project.db.BeginTransaction();
            for (MessageError m : messages) {
                if (project.db.files.containsKey(m.file)) {
                    DBProjectFile file = project.db.files.Data.get(m.file);
                    file.CreateAndAddNewMessage(1, m.value, m.line, m.group);
                    //update file
                    project.db.Update(file);
                }
            }
            project.db.Commit();
            project.db.Disconnect();
        }
    }
    @Override
    public String toString() {
        return Home.getName() + " : " + Utils_.Brackets(description) + " файлы: " + files.size();
    }
}
