/*******************************************************************************
 * Copyright (c) 2012-2017 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.plugin.docker.compose.yaml.deserializer;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.TextNode;

import org.eclipse.che.plugin.docker.compose.ComposeServiceImpl;
import org.eclipse.che.plugin.docker.compose.yaml.ComposeEnvironmentParser;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static java.lang.String.format;
import static java.util.Arrays.asList;

/**
 * Custom deserializer for parsing {@link ComposeServiceImpl#command}
 * to list command words in the yaml compose file.
 * <p>Command can be:</p>
 * <ul>
 * <li>string array:</li>
 *
 * <pre>
 *  image: codenvy/mysql
 *  environment:
 *   MYSQL_USER: petclinic
 *   MYSQL_PASSWORD: password
 *  mem_limit: 2147483648
 *  command: [service, mysql, start]
 * </pre>
 *
 * <li>simple string line:</li>
 *
 * <pre>
 *  image: codenvy/mysql
 *  environment:
 *   MYSQL_USER: petclinic
 *   MYSQL_PASSWORD: password
 *  mem_limit: 2147483648
 *  command: service mysql start
 * </pre>
 * </ul>
 *
 *
 * See more for parsing compose file
 * {@link ComposeEnvironmentParser}.
 * <p>Note: this deserializer works for json too.</p>
 *
 * @author Alexander Andrienko
 */
public class CommandDeserializer extends JsonDeserializer<List<String>> {

    private static final String SPLIT_COMMAND_REGEX = "[ \n\r]+";

    /**
     * Parse command field from the compose yaml file to list command words.
     *
     * @param jsonParser
     *         json parser
     * @param ctxt
     *         deserialization context
     * @throws IOException
     *         in case I/O error. For example element to parsing contains
     *         invalid yaml field type defined for this field by yaml document model.
     * @throws JsonProcessingException
     */
    @Override
    public List<String> deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        TreeNode tree = jsonParser.readValueAsTree();

        if (tree.isArray()) {
            return toCommand((ArrayNode)tree, ctxt);
        }
        if (tree instanceof TextNode) {
            TextNode textNode = (TextNode)tree;
            return asList(textNode.asText().trim().split(SPLIT_COMMAND_REGEX));
        }
        throw ctxt.mappingException((format("Field '%s' must be simple text or string array.", jsonParser.getCurrentName())));
    }

    private List<String> toCommand(ArrayNode arrayCommandNode, DeserializationContext ctxt) throws JsonMappingException {
        List<String> commands = new ArrayList<>();

        for (TreeNode treeNode : arrayCommandNode) {
            if (treeNode instanceof TextNode) {
                TextNode textNode = (TextNode)treeNode;
                commands.add(textNode.asText());
            } else {
                throw ctxt.mappingException("Array 'command' contains not string element.");
            }
        }

        return commands;
    }
}
