/**
 * Copyright (C) 1999-2008 Jive Software. All rights reserved.
 *
 * Licensed 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.jivesoftware.openfire.fastpath.settings.chat;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.xmpp.workgroup.UnauthorizedException;
import org.jivesoftware.xmpp.workgroup.Workgroup;
import org.jivesoftware.xmpp.workgroup.WorkgroupManager;
import org.jivesoftware.xmpp.workgroup.utils.ModelUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;

import com.thoughtworks.xstream.XStream;

/**
 * Creates the default settings for the Web Chat UI. This includes the Web Chat UI
 * images and text settings.
 */
public class ChatSettingsCreator {

	private static final Logger Log = LoggerFactory.getLogger(ChatSettingsCreator.class);

    private static final ChatSettingsCreator instance = new ChatSettingsCreator();

    /**
     * Holds the default images to use.
     */
    private final Map<KeyEnum, String> imageMap = new HashMap<KeyEnum, String>();

    /**
     * Holds the default messages to use in the web client.
     */
    private final Map<KeyEnum, String> textMap = new HashMap<KeyEnum, String>();

    /**
     * Holds the default messages to use by the chatbot.
     */
    private final Map<KeyEnum, String> botMap = new HashMap<KeyEnum, String>();

    /**
     * Holds the label to use in the Admin Console while editing the property.
     */
    private final Map<KeyEnum, String> labelMap = new HashMap<KeyEnum, String>();

    /**
     * Holds the description to use in the Admin Console while editing the property.
     */
    private final Map<String, String> descriptions = new HashMap<String, String>();

    private final ChatSettingsManager chatSettingsManager = ChatSettingsManager.getInstance();

    /**
     * Returns the unique instance of this class.
     *
     * @return instance of class
     */
    public static ChatSettingsCreator getInstance() {
        return instance;
    }

    /**
     * Setups up the individual mappings to use for default settings.
     */
    private ChatSettingsCreator() {
        // Populate Image Map
        imageMap.put(KeyEnum.online_image, "online.gif");
        labelMap.put(KeyEnum.online_image, "Online");

        imageMap.put(KeyEnum.offline_image, "offline.gif");
        labelMap.put(KeyEnum.offline_image, "Offline");

        imageMap.put(KeyEnum.title_logo_image, "logo.gif");
        labelMap.put(KeyEnum.title_logo_image, "Title Logo");

        imageMap.put(KeyEnum.powered_by_image, "poweredBy.gif");
        labelMap.put(KeyEnum.powered_by_image, "Powered By");

        imageMap.put(KeyEnum.background_image, "white_background.gif");
        labelMap.put(KeyEnum.background_image, "Background");

        imageMap.put(KeyEnum.end_button_image, "end_button.gif");
        labelMap.put(KeyEnum.end_button_image, "End Button");

        imageMap.put(KeyEnum.secure_image, "secure_button.gif");
        labelMap.put(KeyEnum.secure_image, "Secure Button");

        imageMap.put(KeyEnum.agent_typing_image, "typing_button.gif");
        labelMap.put(KeyEnum.agent_typing_image, "Agent Typing Button");

        imageMap.put(KeyEnum.send_message_image, "send_button.gif");
        labelMap.put(KeyEnum.send_message_image, "Send Message Button");

        imageMap.put(KeyEnum.send_mail_image, "send_transcript_button.gif");
        labelMap.put(KeyEnum.send_mail_image, "Send Mail Button");

        // Populate Text Map
        populateTextMap();

        // Populate Bot Map
        botMap.put(KeyEnum.welcome_message, "Welcome to the workgroup '${workgroup}'.");
        labelMap.put(KeyEnum.welcome_message, "Greetings");

        botMap.put(KeyEnum.join_question, "Would you like to join the chat, yes or no?");
        labelMap.put(KeyEnum.join_question, "Join question");

        botMap.put(KeyEnum.bye_message, "Thanks for coming. We hope to see you soon again.");
        labelMap.put(KeyEnum.bye_message, "User is leaving");

        botMap.put(KeyEnum.routing_message, "You have entered a waiting queue. An agent will be with you soon");
        labelMap.put(KeyEnum.routing_message, "User has entered a queue");

        botMap.put(KeyEnum.position_message, "Your current position in the queue is ${position}");
        labelMap.put(KeyEnum.position_message, "Inform user position in the queue");

        botMap.put(KeyEnum.departure_confirmed_message, "You have left the waiting queue");
        labelMap.put(KeyEnum.departure_confirmed_message, "User cancelled request to join or request timedout");

        botMap.put(KeyEnum.cannot_join_message, "The workgroup is closed or you are not allowed to enter");
        labelMap.put(KeyEnum.cannot_join_message, "Request to join workgroup was denied");

        botMap.put(KeyEnum.fillout_form_message, "Please, fill out this form to contact an agent");
        labelMap.put(KeyEnum.fillout_form_message, "Inform user that a form must be completed");

        botMap.put(KeyEnum.not_acceptable_message, "Invalid or unknown command. Use !help for more information");
        labelMap.put(KeyEnum.not_acceptable_message, "User sent an unknown or invalid command");

        botMap.put(KeyEnum.not_in_queue_message, "Error, you are not in the waiting queue");
        labelMap.put(KeyEnum.not_in_queue_message, "User asked for his position in the queue but he is not in the queue");

        botMap.put(KeyEnum.workgroup_closed_message, "This workgroup is currently closed");
        labelMap.put(KeyEnum.workgroup_closed_message, "Workgroup is closed");

        botMap.put(KeyEnum.send_email_question, "Do you want to receive the chat transcript by email, yes or no?");
        labelMap.put(KeyEnum.send_email_question, "Email question");

        botMap.put(KeyEnum.send_get_email_question, "Please enter your email address to receive the chat transcript");
        labelMap.put(KeyEnum.send_get_email_question, "Enter Email address");

        botMap.put(KeyEnum.invitation_sent_message, "An invitation to start a chat with an agent has been sent");
        labelMap.put(KeyEnum.invitation_sent_message, "User is being routed to an agent");

        botMap.put(KeyEnum.send_invitation_question, "Do you want to receive another room invitation, yes or no?");
        labelMap.put(KeyEnum.send_invitation_question, "Send invitation again question");

        botMap.put(KeyEnum.invitation_resent_message, "The room invitation was sent again");
        labelMap.put(KeyEnum.invitation_resent_message, "Confirmation that the invitation was sent again");

        botMap.put(KeyEnum.email_sent_message, "Transcript sent to the following email address ${email}");
        labelMap.put(KeyEnum.email_sent_message, "Email was sent to the user");

        botMap.put(KeyEnum.back_command, "!back");
        labelMap.put(KeyEnum.back_command, "Text representing the 'back' command");

        botMap.put(KeyEnum.bye_command, "!bye");
        labelMap.put(KeyEnum.bye_command, "Text representing the 'bye' command");

        botMap.put(KeyEnum.help_command, "!help");
        labelMap.put(KeyEnum.help_command, "Text representing the 'help' command");

        botMap.put(KeyEnum.position_command, "!position");
        labelMap.put(KeyEnum.position_command, "Text representing the 'position' command");

        botMap.put(KeyEnum.repeat_command, "!repeat");
        labelMap.put(KeyEnum.repeat_command, "Text representing the 'repeat' command");

        botMap.put(KeyEnum.back_help_message, "!back - Use this command to return to the previous step.");
        labelMap.put(KeyEnum.back_help_message, "Description of the 'back' command");

        botMap.put(KeyEnum.bye_help_message, "!bye - Use this command to finish the chat.");
        labelMap.put(KeyEnum.bye_help_message, "Description of the 'bye' command");

        botMap.put(KeyEnum.help_help_message, "!help - Use this command to display the list of commands.");
        labelMap.put(KeyEnum.help_help_message, "Description of the 'help' command");

        botMap.put(KeyEnum.position_help_message, "!position - Use this command to learn your position in the queue.");
        labelMap.put(KeyEnum.position_help_message, "Description of the 'position' command");

        botMap.put(KeyEnum.repeat_help_message, "!repeat - Use this command to repeat the last question.");
        labelMap.put(KeyEnum.repeat_help_message, "Description of the 'repeat' command");

        // Define image descriptions

        descriptions.put("Online", "The button to show when agents of this workgroup are online.");
        descriptions.put("Offline", "The button to show when no agents in this workgroup are available to chat or offline.");
        descriptions.put("Title Logo", "Image shown at the upper left hand corner of the client.");
        descriptions.put("Powered By", "Powered by images is shown on the right-bottom corner of the client.");
        descriptions.put("Background", "Image used in the background of the client.");
        descriptions.put("Send Message Button", "The button used in the client to send messages.");
        descriptions.put("Send Mail Button", "The send button to use when sending email messages.");
        descriptions.put("End Button", "The button used to end a chat session.");
        descriptions.put("Secure Button", "Used to show a secure connection has been established.");
        descriptions.put("Agent Typing Button", "Used to show that the agent in the conversation is typing a message.");
    }

    /**
     * Adds the encoded imageMap to the database.
     *
     * @param workgroupJID - the jid of the workgroup to setup.
     */
    private void createImageSettings(JID workgroupJID) {
        PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
        Plugin fastpathPlugin = pluginManager.getPlugin("fastpath");
        File fastpathPluginDirectory = pluginManager.getPluginDirectory(fastpathPlugin);

        File imagesDir = new File(fastpathPluginDirectory, "web/images");

        for (KeyEnum key : imageMap.keySet()) {
            String value = imageMap.get(key);

            File image = new File(imagesDir, value);
            FileInputStream stream = null;
            try {
                stream = new FileInputStream(image);
            }
            catch (FileNotFoundException e) {
                Log.error(e.getMessage(), e);
            }

            if (stream != null) {
                byte[] bytes;
                try {
                    bytes = new byte[(int)image.length()];
                    int read = stream.read(bytes);
                    if (read != bytes.length) {
                        throw new IOException("Failed to read all image bytes."); 
                    }
                    stream.close();
                    final String encodedFile = StringUtils.encodeBase64(bytes);

                    createImageChatSetting(workgroupJID, key,
                            ChatSettings.SettingType.image_settings, encodedFile);
                }
                catch (IOException e) {
                    Log.error(e.getMessage(), e);
                }
            }
        }
    }

    /**
     * Adds the default text settings to the database.
     *
     * @param workgroupJID - the JID of the workgroup to setup.
     */
    private void createTextSettings(JID workgroupJID) {
        for (KeyEnum key : textMap.keySet()) {
            String value = textMap.get(key);
            createChatSetting(workgroupJID, key, ChatSettings.SettingType.text_settings, value);
        }
    }

    /**
     * Adds the default bot settings to the database.
     *
     * @param workgroupJID - the JID of the workgroup to setup.
     */
    private void createBotSettings(JID workgroupJID) {
        try {
            // Enable the workgroup chatbot by default
            Workgroup workgroup = WorkgroupManager.getInstance().getWorkgroup(workgroupJID);
            workgroup.chatbotEnabled(true);
            for (KeyEnum key : botMap.keySet()) {
                String value = botMap.get(key);
                createChatSetting(workgroupJID, key, ChatSettings.SettingType.bot_settings, value);
            }
        }
        catch (UserNotFoundException e) {
            Log.error(e.getMessage(), e);
        }
        catch (UnauthorizedException e) {
            Log.error(e.getMessage(), e);
        }
    }

    private void createChatSetting(JID workgroupJID, KeyEnum key, ChatSettings.SettingType type,
                                   final String value) {
        ChatSetting setting = new ChatSetting(key);
        setting.setWorkgroupNode(workgroupJID.getNode());
        setting.setType(type);
        setting.setValue(value);
        setting.setDefaultValue(value);

        String label = labelMap.get(key);

        if (!ModelUtil.hasLength(label)) {
            label = "";
        }

        String description = descriptions.get(label);
        if (!ModelUtil.hasLength(description)) {
            description = descriptions.get(key.toString());
        }

        if (!ModelUtil.hasLength(description)) {
            description = "";
        }


        setting.setDescription(description);
        setting.setLabel(label);

        chatSettingsManager.addChatSetting(setting);
    }

    private void createImageChatSetting(JID workgroupJID, KeyEnum key,
            ChatSettings.SettingType type, final String value)
    {
        ChatSetting setting = new ChatSetting(key);
        setting.setWorkgroupNode(workgroupJID.getNode());
        setting.setType(type);
        setting.setValue(value);
        setting.setDefaultValue("");

        String label = labelMap.get(key);

        if (!ModelUtil.hasLength(label)) {
            label = "";
        }

        String description = descriptions.get(label);
        if (!ModelUtil.hasLength(description)) {
            description = descriptions.get(key.toString());
        }

        if (!ModelUtil.hasLength(description)) {
            description = "";
        }

        setting.setDescription(description);
        setting.setLabel(label);

        chatSettingsManager.addChatSetting(setting);
    }

    /**
     * Add default settings to a workgroup.
     *
     * @param workgroupJID the full-jid of the workgroup.
     */
    public void createDefaultSettings(JID workgroupJID) {
        final XStream xstream = new XStream();
        xstream.alias("ChatSettings", ChatSettings.class);
        xstream.alias("Key", KeyEnum.class);
        xstream.alias("Setting", ChatSetting.class);

        createImageSettings(workgroupJID);
        createTextSettings(workgroupJID);
        createBotSettings(workgroupJID);
    }


    public void createDefaultSetting(JID workgroupJID, KeyEnum key) {
        if (imageMap.containsKey(key)) {
            String value = imageMap.get(key);
            InputStream stream = getClass().getResourceAsStream("fastpath/imageMap/" + value);
            if (stream == null) {
                stream = getClass().getResourceAsStream("/fastpath/imageMap/" + value);
            }

            if (stream == null) {
                stream = getClass().getResourceAsStream("/fastpath/images/" + value);
            }
            if (stream != null) {
                byte[] bytes;
                try {
                    bytes = new byte[stream.available()];
                    int read = stream.read(bytes);
                    if (read != bytes.length) {
                        throw new IOException("Failed to read all bytes.");
                    }
                    stream.close();
                    final String encodedFile = StringUtils.encodeBase64(bytes);

                    createChatSetting(workgroupJID, key, ChatSettings.SettingType.image_settings,
                            encodedFile);
                }
                catch (IOException e) {
                    Log.error(e.getMessage(), e);
                }
            }
        }
        else if (textMap.containsKey(key)) {
            String value = textMap.get(key);
            createChatSetting(workgroupJID, key, ChatSettings.SettingType.text_settings, value);
        }
        else if (botMap.containsKey(key)) {
            String value = botMap.get(key);
            createChatSetting(workgroupJID, key, ChatSettings.SettingType.bot_settings, value);
        }
    }

    private void populateTextMap() {
        textMap.put(KeyEnum.user_input_page_title, "<b>Start a Live Chat</b><p>Enter your name and all required information.</p>");
        labelMap.put(KeyEnum.user_input_page_title, "User Input Page Greeting");
        descriptions.put(KeyEnum.user_input_page_title.toString(), "The user information text displayed at the top of the page.");


        textMap.put(KeyEnum.start_chat_button, "Start Chat");
        labelMap.put(KeyEnum.start_chat_button, "Start Chat Button");
        descriptions.put(KeyEnum.start_chat_button.toString(), "Text to display on the button to put user into a queue.");


        textMap.put(KeyEnum.queue_title_text, "<b>Routing Your Request...</b><p>Your chat request is being routed. You can cancel your request by clicking \"Close Window\" below.</p>");
        labelMap.put(KeyEnum.queue_title_text, "Title Text In Queue Page");
        descriptions.put(KeyEnum.queue_title_text.toString(), "Text displayed to user while they are waiting in the queue.");

        textMap.put(KeyEnum.queue_description_text, "<div  style=\"border:1px #ccc solid; background-color:#ffe;padding:3px;\"><table border=\"0\"><tr><td><img src=\"images/busy.gif\"></td><td>" +
                "You are currently number ${position} in the queue. It is estimated that your wait time will be ${waitTime}.</td></tr></table></div>");
        labelMap.put(KeyEnum.queue_description_text, "Queue Position Text");
        descriptions.put(KeyEnum.queue_description_text.toString(), "Notifies user of their position in the queue and their estimated wait time.");

        textMap.put(KeyEnum.queue_footer_text, "Please stand by, or you can <a href=\"javascript:showLeaveAMessage()\">leave a message</a>.");
        labelMap.put(KeyEnum.queue_footer_text, "Queue Footer Text");
        descriptions.put(KeyEnum.queue_footer_text.toString(), "Footer text seen in queue.");

        textMap.put(KeyEnum.no_agent_text, "We are unable to route your request at this time. To leave a message or request a call back <a href=\"javascript:showLeaveAMessage()\">click here</a>.");
        labelMap.put(KeyEnum.no_agent_text, "No Agent Available");
        descriptions.put(KeyEnum.no_agent_text.toString(), "Message seen when no agent was able to take the request.");


        textMap.put(KeyEnum.accepted_chat_text, "You are now chatting with ${agent}");
        labelMap.put(KeyEnum.accepted_chat_text, "Chat Room Greeting");
        descriptions.put(KeyEnum.accepted_chat_text.toString(), "The message to display to the user when they first join the room.");


        textMap.put(KeyEnum.transferred_chat_text, "Your conversation is being transferred to another agent.");
        labelMap.put(KeyEnum.transferred_chat_text, "Agent Transfer Message");
        descriptions.put(KeyEnum.transferred_chat_text.toString(), "Message displayed to the user when an agent transfers their chat to another agent.");

        textMap.put(KeyEnum.agent_invite_text, "Inviting another agent to the conversation. Please wait.");
        labelMap.put(KeyEnum.agent_invite_text, "Agent Invitation Message");
        descriptions.put(KeyEnum.agent_invite_text.toString(), "Message displayed to the user when the agent invites another agent into the conversation");

        /*
        textMap.put(KeyEnum.chat_disconnected_text, "Your chat session has ended");
        labelMap.put(KeyEnum.chat_disconnected_text, "Chat session has ended");
        */

        textMap.put(KeyEnum.agent_ends_chat_text, "Your chat session has been ended by ${agent}");
        labelMap.put(KeyEnum.agent_ends_chat_text, "Agent ends the conversation");
        descriptions.put(KeyEnum.agent_ends_chat_text.toString(), "Message displayed to user when their session has been closed by the agent.");

        textMap.put(KeyEnum.transcript_text, "Thank you for chatting with us. Enter your email address below to have a copy of the transcript mailed to you.");
        labelMap.put(KeyEnum.transcript_text, "Message displayed on transcript email page");
        descriptions.put(KeyEnum.transcript_text.toString(), "Message displayed to the user on the email transcript page.");

        textMap.put(KeyEnum.transcript_sent_text, "The transcript has been sent. Thank you for chatting with us.");
        labelMap.put(KeyEnum.transcript_sent_text, "Transcript Sent Message");
        descriptions.put(KeyEnum.transcript_sent_text.toString(), "Message displayed to user when their transcript has been sent successfully");

        textMap.put(KeyEnum.transcript_not_sent_text, "The transcript could not be sent. We are sorry for the inconvenience");
        labelMap.put(KeyEnum.transcript_not_sent_text, "Transcript Send Error");
        descriptions.put(KeyEnum.transcript_not_sent_text.toString(), "Message displayed when a transcript could not be sent.");


        textMap.put(KeyEnum.no_help, "<b>Live Chat Not Available</b><p>Sorry we were not available to handle your request. Please try again later.</p>");
        labelMap.put(KeyEnum.no_help, "No Help Available");
        descriptions.put(KeyEnum.no_help.toString(), "Message displayed when no agent is online and offline settings is not configured.");
    }
}
