/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.ofbiz.base.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * File Utilities
 *
 */
public final class UtilPlist {

    private static final String MODULE = UtilPlist.class.getName();

    private UtilPlist() { }

    /** simple 4 char indentation */
    private static final String INDENT_FOUR_STRING = "    ";

    private static void writePlistProperty(String name, Object value, int indentLevel, PrintWriter writer) {
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.print(name);
        writer.print(" = ");
        if (value instanceof Map<?, ?>) {
            writer.println();
            Map<String, Object> map = UtilGenerics.cast(value);
            writePlistPropertyMap(map, indentLevel, writer, false);
        } else if (value instanceof List<?>) {
            List<Object> list = UtilGenerics.cast(value);
            writePlistPropertyValueList(list, indentLevel, writer);
        } else {
            writer.print(value);
            writer.println(";");
        }
    }
    public static void writePlistPropertyMap(Map<String, Object> propertyMap, int indentLevel, PrintWriter writer, boolean appendComma) {
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.println("{");
        for (Map.Entry<String, Object> property: propertyMap.entrySet()) {
            writePlistProperty(property.getKey(), property.getValue(), indentLevel + 1, writer);
        }
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        if (appendComma) {
            writer.println("},");
        } else {
            writer.println("}");
        }
    }
    private static void writePlistPropertyValueList(List<Object> propertyValueList, int indentLevel, PrintWriter writer) {
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.print("(");

        Iterator<Object> propertyValueIter = propertyValueList.iterator();
        while (propertyValueIter.hasNext()) {
            Object propertyValue = propertyValueIter.next();
            if (propertyValue instanceof Map<?, ?>) {
                Map<String, Object> propertyMap = UtilGenerics.cast(propertyValue);
                writePlistPropertyMap(propertyMap, indentLevel + 1, writer, propertyValueIter.hasNext());
            } else {
                writer.print(propertyValue);
                if (propertyValueIter.hasNext()) {
                    writer.print(", ");
                }
            }
        }

        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.println(");");
    }

    private static void writePlistPropertyXml(String name, Object value, int indentLevel, PrintWriter writer) {
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.print("<key>");
        writer.print(name);
        writer.println("</key>");
        if (value instanceof Map<?, ?>) {
            Map<String, Object> map = UtilGenerics.cast(value);
            writePlistPropertyMapXml(map, indentLevel, writer);
        } else if (value instanceof List<?>) {
            List<Object> list = UtilGenerics.cast(value);
            writePlistPropertyValueListXml(list, indentLevel, writer);
        } else {
            for (int i = 0; i < indentLevel; i++) {
                writer.print(INDENT_FOUR_STRING);
            }
            writer.print("<string>");
            writer.print(value);
            writer.println("</string>");
        }
    }
    private static void writePlistPropertyMapXml(Map<String, Object> propertyMap, int indentLevel, PrintWriter writer) {
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.println("<dict>");
        for (Map.Entry<String, Object> property: propertyMap.entrySet()) {
            writePlistPropertyXml(property.getKey(), property.getValue(), indentLevel + 1, writer);
        }
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.println("</dict>");
    }
    private static void writePlistPropertyValueListXml(List<Object> propertyValueList, int indentLevel, PrintWriter writer) {
        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.println("<array>");

        indentLevel++;
        Iterator<Object> propertyValueIter = propertyValueList.iterator();
        while (propertyValueIter.hasNext()) {
            Object propertyValue = propertyValueIter.next();
            if (propertyValue instanceof Map<?, ?>) {
                Map<String, Object> propertyMap = UtilGenerics.cast(propertyValue);
                writePlistPropertyMapXml(propertyMap, indentLevel, writer);
            } else {
                for (int i = 0; i < indentLevel; i++) {
                    writer.print(INDENT_FOUR_STRING);
                }
                writer.print("<string>");
                writer.print(propertyValue);
                writer.println("</string>");
            }
        }
        indentLevel--;

        for (int i = 0; i < indentLevel; i++) {
            writer.print(INDENT_FOUR_STRING);
        }
        writer.println("</array>");
    }

    /**
     * Writes model information in the Apple EOModelBundle format.
     * For document structure and definition see:
     * http://developer.apple.com/documentation/InternetWeb/Reference/WO_BundleReference/Articles/EOModelBundle.html
     * @param eoModelMap
     * @param eomodeldFullPath
     * @param filename
     * @throws FileNotFoundException
     * @throws UnsupportedEncodingException
     */
    public static void writePlistFile(Map<String, Object> eoModelMap, String eomodeldFullPath, String filename, boolean useXml)
            throws FileNotFoundException, UnsupportedEncodingException {
        try (PrintWriter plistWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
                new File(eomodeldFullPath, filename)), "UTF-8")))) {
            if (useXml) {
                plistWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                plistWriter.println("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/"
                        + "PropertyList-1.0.dtd\">");
                plistWriter.println("<plist version=\"1.0\">");
                writePlistPropertyMapXml(eoModelMap, 0, plistWriter);
                plistWriter.println("</plist>");
            } else {
                writePlistPropertyMap(eoModelMap, 0, plistWriter, false);
            }
        }
    }
}
