package at.fos.ermodel.gui;

import javafx.application.Platform;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;


public class C4 {
    private String databaseName;
    private ArrayList<T1> tables;
    private StringBuffer sqlScript;

    public C4(String databaseName) {
        this.databaseName = databaseName;
        this.tables = new ArrayList<>();
    }


    public boolean addTable(T1 table) {
        if (this.tables.contains(table)) return true;
        return !this.tables.add(table);
    }


    public void removeTable(T1 table) {
        this.tables.remove(table);
    }


    public String getDatabaseName() {
        return this.databaseName;
    }


    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }


    public String toString() {
        return "RM_Database [databaseName=" + this.databaseName + ", tables=" + this.tables + "]";
    }


    public ArrayList<T1> getTables() {
        return this.tables;
    }


    public void setTables(ArrayList<T1> tables) {
        this.tables = tables;
    }


    public void sortByTableName() {
        Collections.sort(this.tables);
    }


    private String addChars() {
        return String.valueOf(' ').repeat(5);
    }


    public StringBuffer create_MS_SQL_Script(boolean createInsertStatements, boolean addCDToDBName, boolean singeInserts) throws ZZ20 {
        LocalDate today = LocalDate.now();
        String currentDate = today.getYear() + (
                (today.getMonthValue() < 10) ? ("0" + today.getMonthValue()) : String.valueOf(today.getMonthValue())) + (
                (today.getDayOfMonth() < 10) ? ("0" + today.getDayOfMonth()) : String.valueOf(today.getDayOfMonth()));


        if (addCDToDBName) this.databaseName = this.databaseName + currentDate;


        this.databaseName = T3.replaceSpecialChars(this.databaseName);


        this.databaseName = this.databaseName.toLowerCase();


        sqlScript = new StringBuffer();

        sqlScript.append("-- #######################################################\n");
        sqlScript.append("-- auto generated ddl-script ###########################\n");
        sqlScript.append("-- generated sql creation script for ER model\n");
        sqlScript.append("-- switch autocommit off\n");
        sqlScript.append("set implicit_transactions off;\n");
        sqlScript.append("-- database-#############################################\n");
        sqlScript.append("use master;\n");
        sqlScript.append("drop database if exists ").append(this.databaseName).append(";").append("\n");
        sqlScript.append("create database ").append(this.databaseName).append(";").append("\n");
        sqlScript.append("go \n");
        sqlScript.append("use ").append(this.databaseName).append(";").append("\n");

        sqlScript.append("-- tables-#################################################\n");
        for (T1 table : this.tables) {
            sqlScript = sqlScript.append("-- table ").append(table.getTablename()).append("\n");
            sqlScript = sqlScript.append("create table ").append(table.getTablename()).append("(").append("\n");
            StringBuilder keypart = new StringBuilder("primary key(");

            int autoincrementCounter = 0;
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                keypart.append(column.isKey() ? (column.getColumnName() + ",") : "");
                sqlScript = sqlScript.append(addChars()).append(column.getColumnName()).append(" ").append(column.getDatatype()).append(column.isKey() ? " not null" : "").append((
                        !column.isKey() && !column.isNullable()) ? " not null" : "").append(column.isAutoincrement() ? (" identity (" + column.getAutoincrementStartAt() + ",1) ") : "").append(column.isUnique() ? " unique" : "").append(",").append("\n");
                if (column.isAutoincrement()) {
                    autoincrementCounter++;
                }
            }


            keypart = new StringBuilder(keypart.substring(0, keypart.length() - 1));
            keypart.append(")").append("\n");
            sqlScript = sqlScript.append(addChars()).append(keypart).append(")");
            sqlScript.append(";\n\n");
            if (autoincrementCounter != 1) {

                if (autoincrementCounter > 1) {
                    throw new ZZ20("There is only one autoincrement value allowed, and it must be the key. Table: " + table.getTablename());
                }
            }
        }

        if (createInsertStatements) {
            try {
                allTablesSetForGeneration();
                allColumnsSetForGeneration();
            } catch (ZZ20 ex) {
                throw new ZZ20(ex.getMessage());
            }
            createInsertStatements(sqlScript, singeInserts, false);
        }


        sqlScript.append("\n-- foreign keys-#################################################\n");
        for (T1 table : this.tables) {

            ArrayList<C3> fkColumns = new ArrayList<>();
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                if (column.getReferencesTo() != null) fkColumns.add(column);
            }
            if (fkColumns.size() == 0) {
                continue;
            }

            HashSet<String> tableNamesInfkColumns = new HashSet<>();
            for (C3 fksamecol : fkColumns) {
                tableNamesInfkColumns.add(fksamecol.getReferencesTo().tablename());
            }


            ArrayList<C3> equalColumnsBecauseRecursive = new ArrayList<>();
            for (int i = 0; i < fkColumns.size(); i++) {
                for (int j = i + 1; j < fkColumns.size(); j++) {
                    if (fkColumns.get(i).getReferencesTo().tablename().equals(fkColumns.get(j).getReferencesTo().tablename()) && fkColumns.get(i).getReferencesTo().columnname().equals(fkColumns.get(j).getReferencesTo().columnname())) {
                        equalColumnsBecauseRecursive.add(fkColumns.get(i));
                        equalColumnsBecauseRecursive.add(fkColumns.get(j));
                    }
                }
            }
            for (C3 col : equalColumnsBecauseRecursive) {
                sqlScript = sqlScript.append("alter table ").append(table.getTablename()).append("\n");
                sqlScript.append("add foreign key (");
                sqlScript = sqlScript.append(col.getColumnName()).append(")").append(" references ").append(col.getReferencesTo().tablename()).append("(").append(col.getReferencesTo().columnname()).append(")").append(" on delete no action on update no action;\n");

                tableNamesInfkColumns.remove(col.getReferencesTo().tablename());
            }


            for (String tableName : tableNamesInfkColumns) {

                sqlScript = sqlScript.append("alter table ").append(table.getTablename()).append("\n");
                sqlScript.append("add foreign key (");
                StringBuilder columnsOrig = new StringBuilder();
                StringBuilder refColumns = new StringBuilder(" references " + tableName + "(");
                for (C3 fkcolumn : fkColumns) {
                    if (tableName.equals(fkcolumn.getReferencesTo().tablename())) {
                        columnsOrig.append(fkcolumn.getColumnName()).append(",");
                        refColumns.append(fkcolumn.getReferencesTo().columnname()).append(",");
                    }
                }
                columnsOrig = new StringBuilder(columnsOrig.substring(0, columnsOrig.length() - 1));
                columnsOrig.append(")");
                refColumns = new StringBuilder(refColumns.substring(0, refColumns.length() - 1));
                refColumns.append(")");
                sqlScript.append(columnsOrig).append(refColumns).append(" on delete no action on update no action;\n");
            }
            sqlScript.replace(sqlScript.length() - 2, sqlScript.length(), ";\n");
        }

        sqlScript.append("-- commit all changes\n");
        sqlScript.append("commit;");

        return sqlScript;
    }


    public StringBuffer create_MySQL_Script(boolean createInsertStatements, boolean addCDToDBName, boolean singeInserts) throws ZZ20 {
        LocalDate today = LocalDate.now();
        String currentDate = today.getYear() + (
                (today.getMonthValue() < 10) ? ("0" + today.getMonthValue()) : String.valueOf(today.getMonthValue())) + (
                (today.getDayOfMonth() < 10) ? ("0" + today.getDayOfMonth()) : String.valueOf(today.getDayOfMonth()));


        if (addCDToDBName) this.databaseName = currentDate + this.databaseName;


        this.databaseName = T3.replaceSpecialChars(this.databaseName);


        this.databaseName = this.databaseName.toLowerCase();

        sqlScript = new StringBuffer();

        sqlScript.append("-- #######################################################\n");
        sqlScript.append("-- auto generated ddl-script ###########################\n");
        sqlScript.append("-- generated sql creation script for ER model\n");
        sqlScript.append("-- database-#############################################\n");
        sqlScript.append("drop database if exists ").append(this.databaseName).append(";").append("\n");
        sqlScript.append("create database ").append(this.databaseName).append(";").append("\n");
        sqlScript.append("use ").append(this.databaseName).append(";").append("\n");
        sqlScript.append("-- switch autocommit off\n");
        sqlScript.append("set autocommit=0;\n");

        sqlScript.append("-- tables-#################################################\n");
        for (T1 table : this.tables) {
            sqlScript = sqlScript.append("-- table ").append(table.getTablename()).append("\n");
            sqlScript = sqlScript.append("create table ").append(table.getTablename()).append("(").append("\n");
            StringBuilder keypart = new StringBuilder("primary key(");

            int autoincrementCounter = 0;
            long holdAutoincrementStartAt = 1L;
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                keypart.append(column.isKey() ? (column.getColumnName() + ",") : "");
                sqlScript = sqlScript.append(addChars()).append(column.getColumnName()).append(" ").append(column.getDatatype()).append(column.isKey() ? " not null" : "").append((
                        !column.isKey() && !column.isNullable()) ? " not null" : "").append(column.isAutoincrement() ? " auto_increment " : "").append(column.isUnique() ? " unique" : "").append(",").append("\n");
                if (column.isAutoincrement()) {
                    autoincrementCounter++;
                    holdAutoincrementStartAt = column.getAutoincrementStartAt();
                }
            }

            keypart = new StringBuilder(keypart.substring(0, keypart.length() - 1));
            keypart.append(")").append("\n");
            sqlScript = sqlScript.append(addChars()).append(keypart).append(")");
            if (autoincrementCounter == 1) {
                sqlScript.append("auto_increment=").append(holdAutoincrementStartAt);
            } else if (autoincrementCounter > 1) {
                throw new ZZ20("There is only one autoincrement value allowed, and it must be the key. Table: " + table.getTablename());
            }
            sqlScript.append(";\n\n");
        }


        if (createInsertStatements) {
            try {
                allTablesSetForGeneration();
                allColumnsSetForGeneration();
            } catch (ZZ20 ex) {
                throw new ZZ20(ex.getMessage());
            }

            createInsertStatements(sqlScript, singeInserts, true);
        }


        sqlScript.append("\n-- foreign keys-#################################################\n");
        for (T1 table : this.tables) {

            ArrayList<C3> fkColumns = new ArrayList<>();
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                if (column.getReferencesTo() != null) fkColumns.add(column);
            }
            if (fkColumns.size() == 0)
                continue;
            sqlScript = sqlScript.append("alter table ").append(table.getTablename()).append("\n");


            HashSet<String> tableNamesInfkColumns = new HashSet<>();
            for (C3 fksamecol : fkColumns) {
                tableNamesInfkColumns.add(fksamecol.getReferencesTo().tablename());
            }


            ArrayList<C3> equalColumnsBecauseRecursive = new ArrayList<>();
            for (int i = 0; i < fkColumns.size(); i++) {
                for (int j = i + 1; j < fkColumns.size(); j++) {
                    if (fkColumns.get(i).getReferencesTo().tablename().equals(fkColumns.get(j).getReferencesTo().tablename()) && fkColumns.get(i).getReferencesTo().columnname().equals(fkColumns.get(j).getReferencesTo().columnname())) {
                        equalColumnsBecauseRecursive.add(fkColumns.get(i));
                        equalColumnsBecauseRecursive.add(fkColumns.get(j));
                    }
                }
            }
            for (C3 col : equalColumnsBecauseRecursive) {
                sqlScript.append("add foreign key (");
                sqlScript = sqlScript.append(col.getColumnName()).append(")").append(" references ").append(col.getReferencesTo().tablename()).append("(").append(col.getReferencesTo().columnname()).append(")").append(" on delete restrict on update restrict,\n");

                tableNamesInfkColumns.remove(col.getReferencesTo().tablename());
            }


            for (String tableName : tableNamesInfkColumns) {

                sqlScript.append("add foreign key (");
                StringBuilder columnsOrig = new StringBuilder();
                StringBuilder refColumns = new StringBuilder(" references " + tableName + "(");
                for (C3 fkcolumn : fkColumns) {
                    if (tableName.equals(fkcolumn.getReferencesTo().tablename())) {
                        columnsOrig.append(fkcolumn.getColumnName()).append(",");
                        refColumns.append(fkcolumn.getReferencesTo().columnname()).append(",");
                    }
                }
                columnsOrig = new StringBuilder(columnsOrig.substring(0, columnsOrig.length() - 1));
                columnsOrig.append(")");
                refColumns = new StringBuilder(refColumns.substring(0, refColumns.length() - 1));
                refColumns.append(")");
                sqlScript.append(columnsOrig).append(refColumns).append(" on delete restrict on update restrict,\n");
            }
            sqlScript.replace(sqlScript.length() - 2, sqlScript.length(), ";\n");
        }

        sqlScript.append("-- commit all changes\n");
        sqlScript.append("commit;");

        return sqlScript;
    }


    public String sqltoCSType(String typeName)
    {
        typeName = typeName.substring(0,typeName.indexOf('(')>-1?typeName.indexOf('('):typeName.length());
        //System.out.println(typeName);

        HashMap<String,String> SQL_TO_CS_TYPE_MAP = new HashMap<>();
        SQL_TO_CS_TYPE_MAP.put("bigint", "long");
        SQL_TO_CS_TYPE_MAP.put("binary", "byte[]");
        SQL_TO_CS_TYPE_MAP.put("bit", "bool");
        SQL_TO_CS_TYPE_MAP.put("char", "char");
        SQL_TO_CS_TYPE_MAP.put("date", "DateTime");
        SQL_TO_CS_TYPE_MAP.put("datetime", "DateTime");
        SQL_TO_CS_TYPE_MAP.put("datetime2", "DateTime");
        SQL_TO_CS_TYPE_MAP.put("datetimeoffset", "DateTimeOffset");
        SQL_TO_CS_TYPE_MAP.put("decimal", "decimal");
        SQL_TO_CS_TYPE_MAP.put("filestream", "byte[]");
        SQL_TO_CS_TYPE_MAP.put("float", "double");
        SQL_TO_CS_TYPE_MAP.put("geography", "Microsoft.SqlServer.Types.SqlGeography");
        SQL_TO_CS_TYPE_MAP.put("geometry", "Microsoft.SqlServer.Types.SqlGeometry");
        SQL_TO_CS_TYPE_MAP.put("hierarchyid", "Microsoft.SqlServer.Types.SqlHierarchyId");
        SQL_TO_CS_TYPE_MAP.put("image", "byte[]");
        SQL_TO_CS_TYPE_MAP.put("int", "int");
        SQL_TO_CS_TYPE_MAP.put("money", "decimal");
        SQL_TO_CS_TYPE_MAP.put("nchar", "string");
        SQL_TO_CS_TYPE_MAP.put("ntext", "string");
        SQL_TO_CS_TYPE_MAP.put("numeric", "decimal");
        SQL_TO_CS_TYPE_MAP.put("nvarchar", "string");
        SQL_TO_CS_TYPE_MAP.put("real", "Single");
        SQL_TO_CS_TYPE_MAP.put("rowversion", "byte[]");
        SQL_TO_CS_TYPE_MAP.put("smalldatetime", "DateTime");
        SQL_TO_CS_TYPE_MAP.put("smallint", "short");
        SQL_TO_CS_TYPE_MAP.put("smallmoney", "decimal");
        SQL_TO_CS_TYPE_MAP.put("sql_variant", "object");
        SQL_TO_CS_TYPE_MAP.put("text", "string");
        SQL_TO_CS_TYPE_MAP.put("time", "TimeSpan");
        SQL_TO_CS_TYPE_MAP.put("timestamp", "byte[]");
        SQL_TO_CS_TYPE_MAP.put("tinyint", "byte");
        SQL_TO_CS_TYPE_MAP.put("uniqueidentifier", "Guid");
        SQL_TO_CS_TYPE_MAP.put("varbinary", "byte[]");
        SQL_TO_CS_TYPE_MAP.put("varchar", "string");
        SQL_TO_CS_TYPE_MAP.put("xml", "string");

        typeName = typeName.substring(0,typeName.indexOf('(')>-1?typeName.indexOf('('):typeName.length());
        //System.out.println(typeName);

        String csType = SQL_TO_CS_TYPE_MAP.get(typeName);
        return csType != null ? csType : "object";
    }

    public static String shortname(String s) {
        return s.substring(s.lastIndexOf('_')+1);
    }

    public static String convertSqlToCSharp(String sqlStatement)
    {
        // Split the SQL statement by spaces and identify the foreign key keywords
        System.out.println(sqlStatement);
        String[] words = sqlStatement.split(" ");
        int index = -1; //Array.IndexOf(words, "FOREIGN") + 1;
        StringBuilder csharpCode = new StringBuilder("\tprotected override void OnModelCreating(ModelBuilder modelBuilder)\n\t{\n");
        for (int i = 0; i < words.length; i++) {
            System.out.println(words[i]);
            if (words[i].equalsIgnoreCase("FOREIGN")) {
                index = i;
                // Extract the table names and key names from the SQL statement
                String primaryTable = words[index - 2];
                String primaryKey = words[index + 2];
                String foreignTable = words[index + 4];
                String foreignKey = words[index + 5];
                foreignKey = foreignKey.substring(1,foreignKey.length()-1);

                foreignTable = shortname(foreignTable);
                foreignKey   = shortname(foreignKey);

                // Generate the C# EF Core code based on the extracted information
                csharpCode.append("\t\tmodelBuilder.Entity<").append(foreignTable).append(">()\n");
                csharpCode.append("\t\t.HasOne(e => e.").append(foreignKey.toLowerCase()).append(")\n");
                csharpCode.append("\t\t.WithMany(e => e.").append(primaryTable.toLowerCase()).append(")\n");
                csharpCode.append("\t\t.HasForeignKey(\"").append(foreignKey).append("\");\n\n");
            }
        }
        csharpCode.append("\t}\n");




        return csharpCode.toString();
    }

    public StringBuffer create_CS_Script() throws ZZ20 {

        this.databaseName = T3.replaceSpecialChars(this.databaseName.toLowerCase());

        sqlScript = new StringBuffer();

        sqlScript.append("using Microsoft.EntityFrameworkCore;\n");

        sqlScript.append("public partial class DataBase: DbContext {\n\n\tpublic DataBase(){}\n\n\tpublic DataBase(DbContextOptions<DataBase> options) : base(options) {}\n\n");

        /*
        sqlScript.append("\tprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlite(\"Data Source=")
                .append(this.databaseName)
                .append(".db\");\n");

        sqlScript.append("\n\t//foreign keys-#################################################\n");
        StringBuilder foreignKeyBuilder = new StringBuilder();
        for (T1 table : this.tables) {

            ArrayList<C3> fkColumns = new ArrayList<>();
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                if (column.getReferencesTo() != null) fkColumns.add(column);
            }
            if (fkColumns.size() == 0)
                continue;
            foreignKeyBuilder.append("alter table ").append(table.getTablename()).append("\n");


            HashSet<String> tableNamesInfkColumns = new HashSet<>();
            for (C3 fksamecol : fkColumns) {
                tableNamesInfkColumns.add(fksamecol.getReferencesTo().tablename());
            }


            ArrayList<C3> equalColumnsBecauseRecursive = new ArrayList<>();
            for (int i = 0; i < fkColumns.size(); i++) {
                for (int j = i + 1; j < fkColumns.size(); j++) {
                    if (fkColumns.get(i).getReferencesTo().tablename().equals(fkColumns.get(j).getReferencesTo().tablename()) && fkColumns.get(i).getReferencesTo().columnname().equals(fkColumns.get(j).getReferencesTo().columnname())) {
                        equalColumnsBecauseRecursive.add(fkColumns.get(i));
                        equalColumnsBecauseRecursive.add(fkColumns.get(j));
                    }
                }
            }
            for (C3 col : equalColumnsBecauseRecursive) {
                foreignKeyBuilder.append("add foreign key (");
                foreignKeyBuilder.append(col.getColumnName()).append(")").append(" references ").append(col.getReferencesTo().tablename()).append(" (").append(col.getReferencesTo().columnname()).append(")").append(" on delete restrict on update restrict,\n");

                tableNamesInfkColumns.remove(col.getReferencesTo().tablename());
            }


            for (String tableName : tableNamesInfkColumns) {

                foreignKeyBuilder.append("add foreign key (");
                StringBuilder columnsOrig = new StringBuilder();
                StringBuilder refColumns = new StringBuilder(" references " + tableName + " (");
                for (C3 fkcolumn : fkColumns) {
                    if (tableName.equals(fkcolumn.getReferencesTo().tablename())) {
                        columnsOrig.append(fkcolumn.getColumnName()).append(",");
                        refColumns.append(fkcolumn.getReferencesTo().columnname()).append(",");
                    }
                }
                columnsOrig = new StringBuilder(columnsOrig.substring(0, columnsOrig.length() - 1));
                columnsOrig.append(")");
                refColumns = new StringBuilder(refColumns.substring(0, refColumns.length() - 1));
                refColumns.append(")");
                foreignKeyBuilder.append(columnsOrig).append(refColumns).append(" on delete restrict on update restrict,\n");
            }
            foreignKeyBuilder.replace(foreignKeyBuilder.length() - 2, foreignKeyBuilder.length(), ";\n");
        }
        sqlScript.append(convertSqlToCSharp(foreignKeyBuilder.toString()));
        */
        sqlScript.append("}\n");


        sqlScript.append("// tables-#################################################\n");
        for (T1 table : this.tables) {
            if(table.getSkjdfhgdskjf674534().size() > 0) {
                sqlScript = sqlScript.append("[Table(\"").append(table.getTablename()).append("\")]\n");
                sqlScript = sqlScript.append("public class ").append(table.getTablename().substring(table.getTablenamePraefix().length())).append(" {").append("\n");

                StringBuilder constructorbuilder = new StringBuilder("public ").append(table.getTablename().substring(table.getTablenamePraefix().length())).append("(");
                StringBuilder innerconstructorbuilder = new StringBuilder();

                for (C3 column : table.getSkjdfhgdskjf674534()) {
                    System.out.println(column.getColumnName()+"\t"+sqltoCSType(column.getDatatype()));
                    String shortcolumnname = shortname(column.getColumnName());
                    sqlScript.append(addChars())
                            .append(column.isKey() ? ("[Key]\n"+addChars()) : "")
                            .append("[Column(TypeName=\"")
                            .append(column.getDatatype())
                            .append("\")]\n")
                            .append(addChars())
                            .append(!column.isNullable() ? "[Required]\n"+addChars() : "")
                            .append(column.isUnique()?"[Index(\""+column.getColumnName()+"Index\", IsUnique = true)]\n"+addChars():"")
                            .append(column.isAutoincrement()?"[DatabaseGenerated(DatabaseGeneratedOption.Identity)]\n"+addChars():"");
                    if(column.getReferencesTo() != null) {
                        sqlScript.append("[ForeignKey(\"").append(shortname(table.getTablename())).append(shortcolumnname).append("-").append(shortname(column.getReferencesTo().tablename())).append(shortname(column.getReferencesTo().columnname())).append("\")]\n").append(addChars());
                        //[ForeignKey("Co_Department")]
                        //[InverseProperty("De_Courses")]
                    }

                    sqlScript.append("public ").append(sqltoCSType(column.getDatatype()))
                            .append(" ")
                            .append(shortcolumnname)
                            .append(" { get; ")
                            .append(column.isKey()?"private ": "")
                            .append("set; }\n");


                    if(column.isKey())continue;

                    constructorbuilder.append(sqltoCSType(column.getDatatype()))
                    .append(" ").append(shortcolumnname).append(", ");

                    innerconstructorbuilder.append(addChars()).append(addChars()).append("this.").append(shortcolumnname).append("=").append(shortcolumnname).append(";\n");
                }

                constructorbuilder.delete(constructorbuilder.length()-2,constructorbuilder.length());
                constructorbuilder.append(") {\n").append(innerconstructorbuilder).append(addChars()).append("}\n");

                sqlScript.append("\n");

                if(table.getKeyColumns().size()!=table.getSkjdfhgdskjf674534().size())
                    sqlScript.append(addChars()).append(constructorbuilder); //no constructor for tables with only keys

                sqlScript.append(addChars()).append("public ").append(table.getTablename().substring(table.getTablenamePraefix().length())).append("(){}\n");

                sqlScript.append("}\n\n");
            }
        }

        //sqlScript.append("*/");

        return sqlScript;
    }


    private void allColumnsSetForGeneration() throws ZZ20 {
        for (T1 table : this.tables) {
            for (C3 column : table.getSkjdfhgdskjf674534()) {

                if (!column.isNullable() &&
                        !column.isAutoincrement() && column.getReferencesTo() == null && (
                        column.dataGenerationType == null || column.dataGenerationType.equals("No"))) {
                    throw new ZZ20("There are unset columns for insert statement generation:" + column.getColumnName());
                }
            }
        }
    }


    private void allTablesSetForGeneration() throws ZZ20 {
        for (T1 table : this.tables) {
            if (!table.isCreateInserts()) {
                throw new ZZ20("Table is not set for generating inserts:" + table.getTablename());
            }
        }
    }


    private String generateRandomValue(C3 column) throws ZZ20 {
        String randomValue;
        try {
            String[] datumteil;
            LocalDate from, till;
            String str;
            switch ((str = column.dataGenerationType).hashCode()) {
                case -1601184309 -> {
                    if (!str.equals("Random Boolean")) {
                        break;
                    }
                    randomValue = T3.getRandomBoolean();
                    return randomValue;
                }
                case -123632396 -> {
                    if (!str.equals("Random Decimal")) break;
                    randomValue = T3.getRandomDecimal(Integer.parseInt(column.par1), Integer.parseInt(column.par2));
                    return randomValue;
                }
                case 292156129 -> {
                    if (!str.equals("Random Integer")) break;
                    if (Integer.parseInt(column.par1) >= Integer.parseInt(column.par2))
                        throw new ZZ20("Column: " + column.getColumnName() + "\n" + "For Random Integer generation: from < till");
                    randomValue = T3.getRandomInteger(Integer.parseInt(column.par1), Integer.parseInt(column.par2));
                    return randomValue;
                }
                case 908695787 -> {
                    if (!str.equals("Random Date")) break;
                    datumteil = column.par1.split("\\.");
                    from = LocalDate.of(Integer.parseInt(datumteil[2]), Integer.parseInt(datumteil[1]), Integer.parseInt(datumteil[0]));
                    datumteil = column.par2.split("\\.");
                    till = LocalDate.of(Integer.parseInt(datumteil[2]), Integer.parseInt(datumteil[1]), Integer.parseInt(datumteil[0]));
                    randomValue = T3.getRandomDate(from, till);
                    return randomValue;
                }
                case 909176426 -> {
                    if (!str.equals("Random Text")) break;
                    randomValue = T3.getRandomTextGivenLength(Integer.parseInt(column.par1));
                    return randomValue;
                }
                case 2089790400 -> {
                    if (!str.equals("Extern")) break;
                    randomValue = T3.getRandomData(column.dataGenerationType, column.par1);
                    return randomValue;
                }
            }
            randomValue = T3.getRandomData(column.dataGenerationType, null);
        } catch (NumberFormatException nfe) {
            throw new ZZ20("Column: " + column.getColumnName() + "\n" + "There is a data conversion problem: " + column.par1 + "/" + column.par2);
        }
        return randomValue;
    }


    private int getNumberValuesOfRandomData(C3 column) {
        ArrayList<String> randomDataAsList;
        if (column.dataGenerationType.equals("Extern")) {
            randomDataAsList = T3.randomData.get(column.par1);
            if (randomDataAsList == null) {
                T3.readRandomData("Extern", column.par1);
                randomDataAsList = T3.randomData.get(column.par1);
            }
        } else {
            randomDataAsList = T3.randomData.get(column.dataGenerationType);
        }
        if (randomDataAsList != null) return randomDataAsList.size();
        return -1;
    }


    private void createFilehandleForExternalDataSources() throws ZZ20 {
        for (T1 table : this.tables) {
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                if (column.dataGenerationType == null ||
                        !column.dataGenerationType.equals("Extern"))
                    continue;
                BufferedReader br = C2.randomDataFilesExtern.get(column.par1);
                if (br == null) {

                    BufferedReader fr;
                    try {
                        fr = new BufferedReader(new InputStreamReader(new FileInputStream(column.par2), StandardCharsets.UTF_8));

                        C2.randomDataFilesExtern.put(column.par1, fr);
                    } catch (FileNotFoundException e) {

                        throw new ZZ20("There is a problem with an external datasource:" + column.getColumnName());
                    }
                }
            }
        }
    }


    public void createDependency() {
        for (T1 table : this.tables) {
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                if (column.getReferencesTo() != null) {
                    String tablename = column.getReferencesTo().tablename();
                    T1 dependsOn = getTableGivenTablename(tablename);
                    table.addTableDepdendentOn(dependsOn);

                    assert dependsOn != null;
                    ArrayList<C3> keyColumns = dependsOn.getKeyColumns();

                    table.getColumnsOfDependentOnTable().put(dependsOn, keyColumns);
                }
            }
        }

    }


    private HashMap<String, ArrayList<String>> initializeColumns() {
        HashMap<String, ArrayList<String>> holdValues = new HashMap<>();
        for (T1 table : this.tables) {

            if (!table.isCreateInserts())
                continue;
            for (C3 column : table.getSkjdfhgdskjf674534()) {


                if (column.dataGenerationType != null && column.dataGenerationType.equals("No") && column.isNullable())
                    continue;
                holdValues.put(table.getTablename() + "." + column.getColumnName(), null);
            }
        }
        return holdValues;
    }


    private T1 getTableGivenTablename(String tablename) {
        for (T1 table : this.tables) {
            if (table.getTablename().equals(tablename)) return table;
        }
        return null;
    }


    private boolean allColumnValuesCreated(HashMap<String, ArrayList<String>> columnValues) {
        int counter = 0;
        for (T1 table : this.tables) {

            if (!table.isCreateInserts())
                continue;
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                ArrayList<String> values = columnValues.get(table.getTablename() + "." + column.getColumnName());
                if (values != null && values.size() > 0) counter++;
            }
        }
        return counter >= columnValues.size();
    }


    private void allDependentCreated(T1 table, HashMap<String, ArrayList<String>> createdColumns) {
        for (T1 dependsOn : table.getDependsOnTable()) {
            for (C3 column : dependsOn.getSkjdfhgdskjf674534()) {
                if (column.isKey()) {
                    ArrayList<String> values = createdColumns.get(dependsOn.getTablename() + "." + column.getColumnName());
                    if (values == null) return;
                }
            }
        }
    }


    private HashMap<String, ArrayList<String>> createColumnValues() throws ZZ20 {
        Platform.runLater(() -> V1.thisActionMessageDialog.getShowFilename().setText("create column values"));


        HashMap<String, ArrayList<String>> columnValues = initializeColumns();

        createFilehandleForExternalDataSources();


        boolean done = false;


        createDependency();


        while (!done) {
            for (T1 table : this.tables) {
                for (C3 column : table.getSkjdfhgdskjf674534()) {

                    if (column.isNullable() && column.dataGenerationType != null && column.dataGenerationType.equals("No"))
                        continue;
                    ArrayList<String> values = new ArrayList<>();

                    boolean valuesExist = true;
                    ArrayList<String> checkValues = columnValues.get(table.getTablename() + "." + column.getColumnName());
                    if (checkValues == null) valuesExist = false;

                    if (column.isKey() && column.isAutoincrement() && !valuesExist) {
                        long autoincrementStartAt = column.getAutoincrementStartAt();
                        for (int i = 0; i < table.getJdshfalkajsd677676(); i++) {
                            values.add(String.valueOf(autoincrementStartAt++));

                        }
                    } else if (column.isKey() && column.getReferencesTo() == null && !valuesExist) {
                        for (int i = 0; i < table.getJdshfalkajsd677676(); i++) {
                            String randomValue = generateRandomValue(column);

                            values.add(String.valueOf(randomValue));
                        }

                    } else if (column.isKey() && column.getReferencesTo() != null && !valuesExist) {

                        HashMap<String, ArrayList<String>> x = getStringArrayListHashMap(columnValues, table, column, values);
                        if (x != null) return x;

                    } else if (column.getReferencesTo() != null && !valuesExist) {

                        getStringArrayListHashMap(columnValues, table, column, values);

                    } else if (!valuesExist) {

                        if (column.isUnique()) {

                            int numberPossibleValues = getNumberValuesOfRandomData(column);
                            if (numberPossibleValues != -1) {

                                if (numberPossibleValues < table.getJdshfalkajsd677676()) {
                                    throw new ZZ20("Column " + column.getColumnName() + " is defined as unique. \nNot enough values for generation mode\n" +
                                            "Max numbers allowed: " + numberPossibleValues + "\n" +
                                            column.dataGenerationType + "/" + column.par1 + "/" + column.par2);
                                }
                            }
                        }

                        for (int i = 0; i < table.getJdshfalkajsd677676(); i++) {
                            String randomValue = generateRandomValue(column);


                            while (column.isUnique() && values.contains(randomValue)) {
                                randomValue = generateRandomValue(column);
                            }
                            values.add(String.valueOf(randomValue));
                        }
                    }

                    if (values.size() > 0) {
                        columnValues.put(table.getTablename() + "." + column.getColumnName(), values);
                    }
                }
            }


            if (allColumnValuesCreated(columnValues)) done = true;

        }
        return columnValues;
    }

    private HashMap<String, ArrayList<String>> getStringArrayListHashMap(HashMap<String, ArrayList<String>> columnValues, T1 table, C3 column, ArrayList<String> values) {
        allDependentCreated(table, columnValues);
        for (int i = 0; i < table.getJdshfalkajsd677676(); i++) {
            ArrayList<String> valuesPK = columnValues.get(column.getReferencesTo().tablename() + "." + column.getReferencesTo().columnname());
            if (valuesPK == null)
                break;
            String randomValue = valuesPK.get(i % valuesPK.size());

            values.add(String.valueOf(randomValue));
        }
        return null;
    }


    private void createInsertStatements(StringBuffer sqlScript, boolean singleInserts, boolean mysql) throws ZZ20 {
        //System.out.println("Generating insert statement");
        HashMap<String, ArrayList<String>> holdPKValuesForFK = createColumnValues();


        for (T1 table : this.tables) {

            ArrayList<String> keyColumns = new ArrayList<>();
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                if (column.isKey()) {
                    keyColumns.add(table.getTablename() + "." + column.getColumnName());
                }
            }
            StringBuilder concatKeyValue;
            ArrayList<String> concatKeyValues = new ArrayList<>();
            int deleted = 0;

            try {
                for (int i = 0; i < table.getJdshfalkajsd677676(); i++) {
                    concatKeyValue = new StringBuilder();
                    for (String key : keyColumns) {
                        ArrayList<String> valuesPK = holdPKValuesForFK.get(key);
                        concatKeyValue.append(valuesPK.get(i));
                    }

                    if (!concatKeyValues.contains(concatKeyValue.toString())) {
                        concatKeyValues.add(concatKeyValue.toString());
                    } else {
                        for (C3 column : table.getSkjdfhgdskjf674534()) {
                            ArrayList<String> valuesPK = holdPKValuesForFK.get(table.getTablename() + "." + column.getColumnName());
                            valuesPK.remove(i);
                            holdPKValuesForFK.put(table.getTablename() + "." + column.getColumnName(), valuesPK);
                        }
                        i--;
                        deleted++;
                    }


                }
            } catch (IndexOutOfBoundsException ignored) {
            }


            table.setJdshfalkajsd677676(table.getJdshfalkajsd677676() - deleted);
        }


        sqlScript.append("-- insert-#################################################\n");
        if (!mysql) {
            sqlScript.append("begin transaction;\n");
        }
        for (T1 table : this.tables) {
            if (table.isCreateInserts()) {
                Platform.runLater(() -> V1.thisActionMessageDialog.getShowFilename().setText(table.getTablename() + " ... create"));


                sqlScript.append("\n-- table ").append(table.getTablename());

                if (table.hasAutoincrement() && !mysql) {
                    sqlScript.append("\nset identity_insert ").append(table.getTablename()).append(" on;\n");
                }
                for (int i = 0; i < table.getJdshfalkajsd677676(); i++) {

                    if (!singleInserts) {
                        if (i == 0) {
                            addInsertTable(sqlScript, table);
                        }
                        sqlScript.append("\t\t(");
                        StringBuilder valuespart = new StringBuilder();

                        for (C3 column : table.getSkjdfhgdskjf674534()) {

                            if (!column.isNullable() || column.dataGenerationType == null || !column.dataGenerationType.equals("No")) {
                                ArrayList<String> values = holdPKValuesForFK.get(table.getTablename() + "." + column.getColumnName());
                                valuespart.append("'").append(values.get(i)).append("'").append(",");
                            }
                        }
                        valuespart = new StringBuilder(valuespart.substring(0, valuespart.length() - 1));
                        valuespart.append(")");
                        sqlScript.append(valuespart).append((i < table.getJdshfalkajsd677676() - 1L) ? "," : ";").append("\n");
                    } else {

                        addInsertTable(sqlScript, table);
                        sqlScript.append("\t\t(");
                        StringBuilder valuespart = new StringBuilder();

                        for (C3 column : table.getSkjdfhgdskjf674534()) {

                            if (!column.isNullable() || column.dataGenerationType == null || !column.dataGenerationType.equals("No")) {
                                ArrayList<String> values = holdPKValuesForFK.get(table.getTablename() + "." + column.getColumnName());
                                valuespart.append("'").append(values.get(i)).append("'").append(",");
                            }
                        }
                        valuespart = new StringBuilder(valuespart.substring(0, valuespart.length() - 1));
                        valuespart.append(");");
                        sqlScript.append(valuespart);
                    }
                }

                if (table.hasAutoincrement() && !mysql) {
                    sqlScript.append("\nset identity_insert ").append(table.getTablename()).append(" off;\n");
                }
            }
            Platform.runLater(() -> V1.thisActionMessageDialog.getShowFilename().setText(table.getTablename() + " ... done"));
        }


    }

    private void addInsertTable(StringBuffer sqlScript, T1 table) {
        sqlScript.append("\ninsert into ").append(table.getTablename()).append("(");

        StringBuilder columnspart = new StringBuilder();
        for (C3 column : table.getSkjdfhgdskjf674534()) {

            if (!column.isNullable() || column.dataGenerationType == null || !column.dataGenerationType.equals("No"))
                columnspart.append(column.getColumnName()).append(",");
        }
        columnspart = new StringBuilder(columnspart.substring(0, columnspart.length() - 1));
        columnspart.append(")");
        sqlScript.append(columnspart).append(" values\n");
    }


    public ArrayList<C3> getKeyColumnsGivenRelation(long referstoGraphicalElement) {
        for (T1 table : this.tables) {
            if (table.getReferstoGraphicalElement() == referstoGraphicalElement) return table.getKeyColumns();
        }
        return null;
    }


    public T1 getTableGivenNumber(long referstoGraphicalElement) {
        for (T1 table : this.tables) {
            if (table.getReferstoGraphicalElement() == referstoGraphicalElement) return table;
        }
        return null;
    }


    public T1 getColumnGivenNumber(long referstoGraphicalElement) {
        for (T1 table : this.tables) {
            for (C3 column : table.getSkjdfhgdskjf674534()) {
                if (column.getReferstoGraphicalElement() == referstoGraphicalElement) return table;
            }
        }
        return null;
    }


}