/*
 *  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.isis.core.runtime.services.i18n.po;

import java.util.SortedMap;
import java.util.SortedSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.isis.applib.services.i18n.TranslationService;
import org.apache.isis.core.runtime.system.context.IsisContext;

class PoWriter extends PoAbstract {

    public static Logger LOG = LoggerFactory.getLogger(PoWriter.class);

    private static class Block {
        private final String msgId;
        private final SortedSet<String> contexts = Sets.newTreeSet();
        private String msgIdPlural;

        private Block(final String msgId) {
            this.msgId = msgId;
        }
    }

    private final SortedMap<String, Block> blocksByMsgId = Maps.newTreeMap();

    public PoWriter(final TranslationServicePo translationServicePo) {
        super(translationServicePo, TranslationService.Mode.WRITE);
    }

    //region > shutdown

    @Override
    void shutdown() {
        if(IsisContext.getMetaModelInvalidExceptionIfAny() != null) {
            // suppress logging translations
            return;
        }
        logTranslations();
    }

    private void logTranslations() {
        final StringBuilder buf = new StringBuilder();

        buf.append("\n");
        buf.append("\n##############################################################################");
        buf.append("\n#");
        buf.append("\n# .pot file");
        buf.append("\n#");
        buf.append("\n# Translate this file to each required language and place in WEB-INF, eg:");
        buf.append("\n#");
        buf.append("\n#     /WEB-INF/translations-en_US.po");
        buf.append("\n#     /WEB-INF/translations-en.po");
        buf.append("\n#     /WEB-INF/translations-fr_FR.po");
        buf.append("\n#     /WEB-INF/translations-fr.po");
        buf.append("\n#     /WEB-INF/translations.po");
        buf.append("\n#");
        buf.append("\n# If the app uses TranslatableString (eg for internationalized validation");
        buf.append("\n# messages), or if the app calls the TranslationService directly, then ensure");
        buf.append("\n# that all text to be translated has been captured by running a full");
        buf.append("\n# integration test suite that exercises all relevant behaviour");
        buf.append("\n#");
        buf.append("\n##############################################################################");
        buf.append("\n");
        buf.append("\n");
        toPot(buf);
        buf.append("\n");
        buf.append("\n");
        buf.append("\n##############################################################################");
        buf.append("\n# end of .pot file");
        buf.append("\n##############################################################################");
        buf.append("\n");
        LOG.info(buf.toString());
    }

    /**
     * As per <a href="http://pology.nedohodnik.net/doc/user/en_US/ch-poformat.html">section 2.6</a>.
     */
    protected void header(final StringBuilder buf) {
        final String createdAt = LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss+Z");
        buf.append("#, fuzzy").append("\n");
        buf.append("msgid \"\"").append("\n");
        buf.append("msgstr \"\"").append("\n");
        buf.append("\"Project-Id-Version: \\n\"").append("\n");
        buf.append("\"POT-Creation-Date: ").append(createdAt).append("\\n\"").append("\n");
        buf.append("\"MIME-Version: 1.0\\n\"").append("\n");
        buf.append("\"Content-Type: text/plain; charset=UTF-8\\n\"").append("\n");
        buf.append("\"Content-Transfer-Encoding: 8bit\\n\"").append("\n");
        buf.append("\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"").append("\n");
        buf.append("\n\n");
    }
    //endregion


    public String translate(final String context, final String msgId) {

        if(msgId == null) {
            return null;
        }
        final Block block = blockFor(msgId);
        block.contexts.add(context);

        return msgId;
    }

    @Override
    String translate(final String context, final String msgId, final String msgIdPlural, final int num) {

        if(msgId == null) {
            return null;
        }
        final Block block = blockFor(msgId);
        block.contexts.add(context);
        block.msgIdPlural = msgIdPlural;

        return null;
    }

    private synchronized Block blockFor(final String msgId) {
        Block block = blocksByMsgId.get(msgId);
        if(block == null) {
            block = new Block(msgId);
            blocksByMsgId.put(msgId, block);
        }
        return block;
    }

    void toPot(final StringBuilder buf) {
        header(buf);
        for (final String msgId : blocksByMsgId.keySet()) {
            final Block block = blocksByMsgId.get(msgId);
            for (final String context : block.contexts) {
                buf.append("#: ").append(context).append("\n");
            }
            buf.append("msgid \"").append(escape(msgId)).append("\"\n");
            String msgIdPlural = block.msgIdPlural;
            if(msgIdPlural == null) {
                buf.append("msgstr \"\"\n");
            } else {
                buf.append("msgid_plural \"").append(escape(msgIdPlural)).append("\"\n");
                buf.append("msgstr[0] \"\"\n");
                buf.append("msgstr[1] \"\"\n");
            }
            buf.append("\n\n");
        }
    }

    static String escape(final String msgId) {
        return msgId.replace("\"", "\\\"");
    }

}
