package cz.tomaskypta.tools.langtool.exporting;

import java.io.*;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.util.CellRangeAddress;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


public class ToolExport {

    private static final String DIR_VALUES = "values";
    private static final String[] POTENTIAL_RES_DIRS = new String[]{"res", "src/main/res"};

    private DocumentBuilder builder;
    private File outExcelFile;
    private String project;
    private Map<String, Integer> keysIndex;
    private PrintStream out;
    private ExportConfig mConfig;
    private Set<String> sAllowedFiles = new HashSet<String>();

    {
        sAllowedFiles.add("strings.xml");
    }

    public ToolExport(PrintStream out) throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        builder = dbf.newDocumentBuilder();
        this.out = out == null ? System.out : out;
    }

    public static void run(ExportConfig config) throws SAXException,
        IOException, ParserConfigurationException {
        run(null, config);
    }

    public static void run(PrintStream out, ExportConfig config) throws SAXException, IOException, ParserConfigurationException {
        ToolExport tool = new ToolExport(out);
        if (StringUtils.isEmpty(config.inputExportProject)) {
            tool.out.println("Cannot export, missing config");
            return;
        }
        File project = new File(config.inputExportProject);
        if (StringUtils.isEmpty(config.outputFile)) {
            config.outputFile = "exported_strings_" + System.currentTimeMillis() + ".xls";
        }
        tool.outExcelFile = new File(config.outputFile);
        tool.project = project.getName();
        tool.mConfig = config;
        tool.sAllowedFiles.addAll(config.additionalResources);
        tool.export(project);
    }

    private void export(File project) throws SAXException, IOException {
        File res = findResourceDir(project);
        if (res == null) {
            System.err.println("Cannot find resource directory.");
            return;
        }
        for (File dir : res.listFiles()) {
            if (!dir.isDirectory() || !dir.getName().startsWith(DIR_VALUES)) {
                continue;
            }
            String dirName = dir.getName();
            if (dirName.equals(DIR_VALUES)) {
                keysIndex = exportDefLang(dir);
            } else {
                int index = dirName.indexOf('-');
                if (index == -1)
                    continue;
                String lang = dirName.substring(index + 1);
                exportLang(lang, dir);
            }
        }
    }

    private File findResourceDir(File project) {
        List<File> availableResDirs = new LinkedList<File>();
        for (String potentialResDir : POTENTIAL_RES_DIRS) {
            File res = new File(project, potentialResDir);
            if (res.exists()) {
                availableResDirs.add(res);
            }
        }
        if (!availableResDirs.isEmpty()) {
            return availableResDirs.get(0);
        }
        return null;
    }

    private void exportLang(String lang, File valueDir) throws IOException, SAXException {
        for (String fileName : sAllowedFiles) {
            File stringFile = new File(valueDir, fileName);
            if (!stringFile.exists()) {
                continue;
            }
            exportLangToExcel(project, lang, stringFile, getStrings(stringFile), outExcelFile, keysIndex);
        }
    }

    private Map<String, Integer> exportDefLang(File valueDir) throws IOException, SAXException {
        Map<String, Integer> keys = new HashMap<String, Integer>();
        HSSFWorkbook wb = new HSSFWorkbook();

        HSSFSheet sheet;
        sheet = wb.createSheet(project);
        int rowIndex = 0;
        sheet.createRow(rowIndex++);
        createTilte(wb, sheet);
        addLang2Tilte(wb, sheet, "default");
        sheet.createFreezePane(1, 1);

        FileOutputStream outFile = new FileOutputStream(outExcelFile);
        wb.write(outFile);
        outFile.close();

        for (String fileName : sAllowedFiles) {
            File stringFile = new File(valueDir, fileName);
            if (!stringFile.exists()) {
                continue;
            }
            keys.putAll(exportDefLangToExcel(rowIndex, project, stringFile, getStrings(stringFile), outExcelFile));
        }


        return keys;
    }

    private NodeList getStrings(File f) throws SAXException, IOException {
        Document dom = builder.parse(f);
        return dom.getDocumentElement().getChildNodes();
    }

    private static HSSFCellStyle createTilteStyle(HSSFWorkbook wb) {
        HSSFFont bold = wb.createFont();
        bold.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        HSSFCellStyle style = wb.createCellStyle();
        style.setFont(bold);
        style.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        style.setWrapText(true);

        return style;
    }

    private static HSSFCellStyle createCommentStyle(HSSFWorkbook wb) {

        HSSFFont commentFont = wb.createFont();
        commentFont.setColor(HSSFColor.GREEN.index);
        commentFont.setItalic(true);
        commentFont.setFontHeightInPoints((short)12);

        HSSFCellStyle commentStyle = wb.createCellStyle();
        commentStyle.setFont(commentFont);
        return commentStyle;
    }

    private static HSSFCellStyle createPlurarStyle(HSSFWorkbook wb) {

        HSSFFont commentFont = wb.createFont();
        commentFont.setColor(HSSFColor.GREY_50_PERCENT.index);
        commentFont.setItalic(true);
        commentFont.setFontHeightInPoints((short)12);

        HSSFCellStyle commentStyle = wb.createCellStyle();
        commentStyle.setFont(commentFont);
        return commentStyle;
    }

    private static HSSFCellStyle createKeyStyle(HSSFWorkbook wb) {
        HSSFFont bold = wb.createFont();
        bold.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        bold.setFontHeightInPoints((short)11);

        HSSFCellStyle keyStyle = wb.createCellStyle();
        keyStyle.setFont(bold);

        return keyStyle;
    }

    private static HSSFCellStyle createTextStyle(HSSFWorkbook wb) {
        HSSFFont plain = wb.createFont();
        plain.setFontHeightInPoints((short)12);

        HSSFCellStyle textStyle = wb.createCellStyle();
        textStyle.setFont(plain);

        return textStyle;
    }

    private static HSSFCellStyle createMissedStyle(HSSFWorkbook wb) {

        HSSFCellStyle style = wb.createCellStyle();
        style.setFillForegroundColor(HSSFColor.RED.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);

        return style;
    }

    private static void createTilte(HSSFWorkbook wb, HSSFSheet sheet) {
        HSSFRow titleRow = sheet.getRow(0);

        HSSFCell cell = titleRow.createCell(0);
        cell.setCellStyle(createTilteStyle(wb));
        cell.setCellValue("KEY");

        sheet.setColumnWidth(cell.getColumnIndex(), (40 * 256));
    }

    private static void addLang2Tilte(HSSFWorkbook wb, HSSFSheet sheet, String lang) {
        HSSFRow titleRow = sheet.getRow(0);
        HSSFCell lastCell = titleRow.getCell((int)titleRow.getLastCellNum() - 1);
        if (lang.equals(lastCell.getStringCellValue())) {
            // language column already exists
            return;
        }
        HSSFCell cell = titleRow.createCell((int)titleRow.getLastCellNum());
        cell.setCellStyle(createTilteStyle(wb));
        cell.setCellValue(lang);

        sheet.setColumnWidth(cell.getColumnIndex(), (60 * 256));
    }


    private Map<String, Integer> exportDefLangToExcel(int rowIndex, String project, File src, NodeList strings, File f) throws FileNotFoundException, IOException {
        out.println();
        out.println("Start processing DEFAULT language " + src.getName());

        Map<String, Integer> keys = new HashMap<String, Integer>();

        HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(f));

        HSSFCellStyle commentStyle = createCommentStyle(wb);
        HSSFCellStyle plurarStyle = createPlurarStyle(wb);
        HSSFCellStyle keyStyle = createKeyStyle(wb);
        HSSFCellStyle textStyle = createTextStyle(wb);

        HSSFSheet sheet = wb.getSheet(project);


        for (int i = 0; i < strings.getLength(); i++) {
            Node item = strings.item(i);
            if (item.getNodeType() == Node.TEXT_NODE) {

            }
            if (item.getNodeType() == Node.COMMENT_NODE) {
                HSSFRow row = sheet.createRow(rowIndex++);
                HSSFCell cell = row.createCell(0);
                cell.setCellValue(String.format("/** %s **/", item.getTextContent()));
                cell.setCellStyle(commentStyle);

                sheet.addMergedRegion(new CellRangeAddress(row.getRowNum(), row.getRowNum(), 0, 255));
            }

            if ("string".equals(item.getNodeName())) {
                Node translatable = item.getAttributes().getNamedItem("translatable");
                if (translatable != null && "false".equals(translatable.getNodeValue())) {
                    continue;
                }
                String key = item.getAttributes().getNamedItem("name").getNodeValue();
                if (mConfig.isIgnoredKey(key)) {
                    continue;
                }
                keys.put(key, rowIndex);

                HSSFRow row = sheet.createRow(rowIndex++);

                HSSFCell cell = row.createCell(0);
                cell.setCellValue(key);
                cell.setCellStyle(keyStyle);

                cell = row.createCell(1);
                cell.setCellStyle(textStyle);
                cell.setCellValue(item.getTextContent());
            } else if ("plurals".equals(item.getNodeName())) {
                String key = item.getAttributes().getNamedItem("name").getNodeValue();
                if (mConfig.isIgnoredKey(key)) {
                    continue;
                }
                String plurarName = key;

                HSSFRow row = sheet.createRow(rowIndex++);
                HSSFCell cell = row.createCell(0);
                cell.setCellValue(String.format("//plurals: %s", plurarName));
                cell.setCellStyle(plurarStyle);

                NodeList items = item.getChildNodes();
                for (int j = 0; j < items.getLength(); j++) {
                    Node plurarItem = items.item(j);
                    if ("item".equals(plurarItem.getNodeName())) {
                        String itemKey = plurarName + "#" + plurarItem.getAttributes().getNamedItem("quantity").getNodeValue();
                        keys.put(itemKey, rowIndex);

                        HSSFRow itemRow = sheet.createRow(rowIndex++);

                        HSSFCell itemCell = itemRow.createCell(0);
                        itemCell.setCellValue(itemKey);
                        itemCell.setCellStyle(keyStyle);

                        itemCell = itemRow.createCell(1);
                        itemCell.setCellStyle(textStyle);
                        itemCell.setCellValue(plurarItem.getTextContent());
                    }
                }
            } else if ("string-array".equals(item.getNodeName())) {
                String key = item.getAttributes().getNamedItem("name").getNodeValue();
                if (mConfig.isIgnoredKey(key)) {
                    continue;
                }
                NodeList arrayItems = item.getChildNodes();
                for (int j = 0, k = 0; j < arrayItems.getLength(); j++) {
                    Node arrayItem = arrayItems.item(j);
                    if ("item".equals(arrayItem.getNodeName())) {
                        String itemKey = key + "[" + k++ + "]";
                        keys.put(itemKey, rowIndex);

                        HSSFRow itemRow = sheet.createRow(rowIndex++);

                        HSSFCell itemCell = itemRow.createCell(0);
                        itemCell.setCellValue(itemKey);
                        itemCell.setCellStyle(keyStyle);

                        itemCell = itemRow.createCell(1);
                        itemCell.setCellStyle(textStyle);
                        itemCell.setCellValue(arrayItem.getTextContent());
                    }
                }
            }
        }

        FileOutputStream outFile = new FileOutputStream(f);
        wb.write(outFile);
        outFile.close();

        out.println("DEFAULT language was precessed");
        return keys;
    }

    private void exportLangToExcel(String project, String lang, File src, NodeList strings, File f, Map<String, Integer> keysIndex) throws FileNotFoundException, IOException {
        out.println();
        out.println(String.format("Start processing: '%s'", lang) + " " + src.getName());
        Set<String> missedKeys = new HashSet<String>(keysIndex.keySet());

        HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(f));

        HSSFCellStyle textStyle = createTextStyle(wb);

        HSSFSheet sheet = wb.getSheet(project);
        addLang2Tilte(wb, sheet, lang);

        HSSFRow titleRow = sheet.getRow(0);
        int lastColumnIdx = (int)titleRow.getLastCellNum() - 1;

        for (int i = 0; i < strings.getLength(); i++) {
            Node item = strings.item(i);

            if ("string".equals(item.getNodeName())) {
                Node translatable = item.getAttributes().getNamedItem("translatable");
                if (translatable != null && "false".equals(translatable.getNodeValue())) {
                    continue;
                }
                String key = item.getAttributes().getNamedItem("name").getNodeValue();
                Integer index = keysIndex.get(key);
                if (index == null) {
                    out.println("\t" + key + " - row does not exist");
                    continue;
                }

                missedKeys.remove(key);
                HSSFRow row = sheet.getRow(index);

                HSSFCell cell = row.createCell(lastColumnIdx);
                cell.setCellValue(item.getTextContent());
                cell.setCellStyle(textStyle);
            } else if ("plurals".equals(item.getNodeName())) {
                String key = item.getAttributes().getNamedItem("name").getNodeValue();
                String plurarName = key;

                NodeList items = item.getChildNodes();
                for (int j = 0; j < items.getLength(); j++) {
                    Node plurarItem = items.item(j);
                    if ("item".equals(plurarItem.getNodeName())) {
                        key = plurarName + "#" + plurarItem.getAttributes().getNamedItem("quantity").getNodeValue();
                        Integer index = keysIndex.get(key);
                        if (index == null) {
                            out.println("\t" + key + " - row does not exist");
                            continue;
                        }
                        missedKeys.remove(key);

                        HSSFRow row = sheet.getRow(index);

                        HSSFCell cell = row.createCell(lastColumnIdx);
                        cell.setCellValue(plurarItem.getTextContent());
                        cell.setCellStyle(textStyle);
                    }
                }
            } else if ("string-array".equals(item.getNodeName())) {
                String key = item.getAttributes().getNamedItem("name").getNodeValue();
                NodeList arrayItems = item.getChildNodes();
                for (int j = 0, k = 0; j < arrayItems.getLength(); j++) {
                    Node arrayItem = arrayItems.item(j);
                    if ("item".equals(arrayItem.getNodeName())) {
                        String itemKey = key + "[" + k++ + "]";
                        Integer rowIndex = keysIndex.get(itemKey);
                        if (rowIndex == null) {
                            out.println("\t" + key + " - row does not exist");
                            continue;
                        }
                        missedKeys.remove(key);

                        HSSFRow itemRow = sheet.getRow(rowIndex);

                        HSSFCell cell = itemRow.createCell(lastColumnIdx);
                        cell.setCellValue(arrayItem.getTextContent());
                        cell.setCellStyle(textStyle);
                    }
                }
            }
        }

        HSSFCellStyle missedStyle = createMissedStyle(wb);

        if (!missedKeys.isEmpty()) {
            out.println("  MISSED KEYS:");
        }
        for (String missedKey : missedKeys) {
            out.println("\t" + missedKey);
            Integer index = keysIndex.get(missedKey);
            HSSFRow row = sheet.getRow(index);
            HSSFCell cell = row.createCell((int)row.getLastCellNum());
            cell.setCellStyle(missedStyle);
        }

        FileOutputStream outStream = new FileOutputStream(f);
        wb.write(outStream);
        outStream.close();

        if (missedKeys.isEmpty()) {
            out.println(String.format("'%s' was processed", lang));
        } else {
            out.println(String.format("'%s' was processed with MISSED KEYS - %d", lang, missedKeys.size()));
        }
    }
}