/*
 * Copyright 2018 ThoughtWorks, Inc.
 *
 * 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 com.thoughtworks.go.domain;

import com.thoughtworks.go.util.GoConstants;
import com.thoughtworks.go.util.TimeProvider;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.lang3.StringUtils.isBlank;

public class GoConfigRevision {
    private static final String DELIMITER_CHAR = "|";
    private static final String DELIMITER = "\\" + DELIMITER_CHAR;
    private static final String VALUE = "(([^" + DELIMITER_CHAR + "]|" + DELIMITER + DELIMITER + ")+)";
    private static final int GROUPS_IN_VALUE_MATCHER = 2; //the VALUE matcher has one subgroup per group 1 + 1 = 2

    public static enum Fragment {
        user, timestamp, schema_version, go_edition, go_version, md5;

        public String represent(String value) {
            return toString() + ":" + value;
        }

        private int offset() {
            return ArrayUtils.indexOf(values(), this);
        }

        private static String string(String value, String delimiter) {
            List<String> parts = new ArrayList<>(0);
            for (Fragment fragment : values()) {
                parts.add(fragment.represent(value));
            }
            return StringUtils.join(parts, delimiter);
        }

        private String unesc(String escapedValue) {
            return escapedValue.replaceAll(DELIMITER + DELIMITER, DELIMITER_CHAR);
        }

        private String getMatch(Matcher matcher) {
            return unesc(matcher.group(offset()*GROUPS_IN_VALUE_MATCHER + 1));
        }
    }

    private static final Pattern PATTERN = Pattern.compile("^" + Fragment.string(VALUE, DELIMITER) + "$");


    private String md5;
    private String username;
    private String goVersion;
    private String goEdition;
    private String xml;
    private byte[] configXmlBytes;
    private Date time;
    private int schemaVersion;
	private String commitSHA;

    public GoConfigRevision(String configXml, String md5, String username, String goVersion, TimeProvider provider) {
        this(configXml);
        this.md5 = md5;
        this.username = username;
        this.goVersion = goVersion;
        this.goEdition = "OpenSource";
        this.time = provider.currentTime();
        this.schemaVersion = GoConstants.CONFIG_SCHEMA_VERSION;
    }

    public GoConfigRevision(byte[] configXml, String comment) {
        this((String) null, comment);
        this.configXmlBytes = configXml;
    }

    public GoConfigRevision(String configXml, String comment) {
        this(configXml);
        Matcher matcher = PATTERN.matcher(comment);
        if (matcher.matches()) {
            username = Fragment.user.getMatch(matcher);
            time = new Date(Long.parseLong(Fragment.timestamp.getMatch(matcher)));
            schemaVersion = Integer.parseInt(Fragment.schema_version.getMatch(matcher));
            goEdition = Fragment.go_edition.getMatch(matcher);
            goVersion = Fragment.go_version.getMatch(matcher);
            md5 = Fragment.md5.getMatch(matcher);
        } else {
            throw new IllegalArgumentException(String.format("failed to parse comment [%s]", comment));
        }
    }


    private GoConfigRevision(String configXml) {
        this.configXmlBytes = null;
        this.xml = configXml;
    }

    public String getMd5() {
        return md5;
    }

    public String getContent() {
        if (isBlank(xml)) {
            if (ArrayUtils.isEmpty(configXmlBytes)) {
                xml = "";
            } else {
                xml = new String(configXmlBytes, UTF_8);
            }
        }
        return xml;
    }

    public boolean isByteArrayBacked() {
        return configXmlBytes != null;
    }

    public byte[] getConfigXmlBytes() {
        return configXmlBytes;
    }

    public String getComment() {
        return String.format(Fragment.string("%s", DELIMITER_CHAR), esc(username), esc(time.getTime()), esc(schemaVersion), esc(goEdition), esc(goVersion), esc(md5));
    }

    public static String esc(Object content) {
        return content.toString().replaceAll(DELIMITER, DELIMITER_CHAR + DELIMITER_CHAR);
    }

    public String getGoVersion() {
        return goVersion;
    }

    public String getGoEdition() {
        return goEdition;
    }

    public String getUsername() {
        return username;
    }

    public Date getTime() {
        return time;
    }

    public int getSchemaVersion() {
        return schemaVersion;
    }

	public String getCommitSHA() {
		return commitSHA;
	}

	public void setCommitSHA(String commitSHA) {
		this.commitSHA = commitSHA;
	}

	@Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        GoConfigRevision that = (GoConfigRevision) o;

        if (md5 != null ? !md5.equals(that.md5) : that.md5 != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        return md5 != null ? md5.hashCode() : 0;
    }

    @Override public String toString() {
        return new ToStringBuilder(this).
                append("time", time).
                append("md5", md5).
                append("username", username).
                append("goVersion", goVersion).
                append("goEdition", goEdition).
                append("xml", xml).
                append("schemaVersion", schemaVersion).
                toString();
    }
}

@@ -4345,10 +4345,12 @@
 l = 
-%22%22
+null
 ;%0A  

Fix build