/**
 * Copyright (C) 2014 SignalFuse, Inc.
 */
package com.github.dockerjava.core;

import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.core.exception.InvalidRepositoryNameException;

@SuppressWarnings(value = "checkstyle:equalshashcode")
public class NameParser {
    private NameParser() {
    }

    // CHECKSTYLE:OFF
    private static final int RepositoryNameTotalLengthMax = 255;

    private static final String SHA256_SEPARATOR = "@sha256:";
    private static final String COLON_SEPARATOR = ":";

    private static final Pattern RepositoryNameComponentRegexp = Pattern.compile("[a-z0-9]+(?:[._-][a-z0-9]+)*");

    private static final Pattern RepositoryNameComponentAnchoredRegexp = Pattern.compile("^"
            + RepositoryNameComponentRegexp.pattern() + "$");

    // CHECKSTYLE:ON

    // private static final Pattern RepositoryNameRegexp = Pattern.compile("(?:" +
    // RepositoryNameComponentRegexp.pattern()
    // + "/)*" + RepositoryNameComponentRegexp.pattern());

    public static ReposTag parseRepositoryTag(String name) {
        int n = name.lastIndexOf(':');
        if (n < 0) {
            return new ReposTag(name, "");
        }
        String tag = name.substring(n + 1);
        if (StringUtils.containsIgnoreCase(name, SHA256_SEPARATOR)) {
            return new ReposTag(name, "");
        }
        if (!tag.contains("/")) {
            return new ReposTag(name.substring(0, n), tag);
        }
        return new ReposTag(name, "");
    }

    public static class ReposTag {
        public final String repos;

        public final String tag;

        public ReposTag(String repos, String tag) {
            this.repos = repos;
            this.tag = tag;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof ReposTag) {
                ReposTag other = (ReposTag) obj;
                return new EqualsBuilder().append(repos, other.repos).append(tag, other.tag).isEquals();
            } else {
                return false;
            }
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);
        }
    }

    /*
     * see https://github.com/docker/distribution/blob/master/registry/api/v2/names.go
     */
    public static void validateRepoName(String name) {
        if (name.isEmpty()) {
            throw new InvalidRepositoryNameException(String.format("Invalid empty repository name \"%s\"", name));
        }

        if (name.length() > RepositoryNameTotalLengthMax) {
            throw new InvalidRepositoryNameException(String.format("Repository name \"%s\" is longer than "
                    + RepositoryNameTotalLengthMax, name));
        }

        String[] components = name.split("/");

        for (String component : components) {
            if (!RepositoryNameComponentAnchoredRegexp.matcher(component).matches()) {
                throw new InvalidRepositoryNameException(String.format(
                        "Repository name \"%s\" is invalid. Component: %s", name, component));
            }
        }
    }

    public static HostnameReposName resolveRepositoryName(String reposName) {
        if (reposName.contains("://")) {
            throw new InvalidRepositoryNameException("RepositoryName shouldn't contain a scheme");
        }

        String[] nameParts = reposName.split("/", 2);
        if (nameParts.length == 1
                || (!nameParts[0].contains(".") && !nameParts[0].contains(":") && !nameParts[0].equals("localhost"))) {
            if (StringUtils.containsIgnoreCase(reposName, SHA256_SEPARATOR)) {
                reposName = StringUtils.substringBeforeLast(reposName, SHA256_SEPARATOR);
            }

            if (StringUtils.contains(reposName, COLON_SEPARATOR)) {
                reposName = StringUtils.substringBeforeLast(reposName, COLON_SEPARATOR);
            }
            return new HostnameReposName(AuthConfig.DEFAULT_SERVER_ADDRESS, reposName);
        }

        String hostname = nameParts[0];
        reposName = nameParts[1];
        if (hostname.contains("index.docker.io")) {
            throw new InvalidRepositoryNameException(String.format("Invalid repository name, try \"%s\" instead",
                    reposName));
        }
        if (StringUtils.containsIgnoreCase(reposName, SHA256_SEPARATOR)) {
            reposName = StringUtils.substringBeforeLast(reposName, SHA256_SEPARATOR);
        }

        if (StringUtils.contains(reposName, COLON_SEPARATOR)) {
            reposName = StringUtils.substringBeforeLast(reposName, COLON_SEPARATOR);
        }

        validateRepoName(reposName);
        return new HostnameReposName(hostname, reposName);
    }

    public static class HostnameReposName {
        public final String hostname;

        public final String reposName;

        public HostnameReposName(String hostname, String reposName) {
            this.hostname = hostname;
            this.reposName = reposName;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof HostnameReposName) {
                HostnameReposName other = (HostnameReposName) obj;
                return new EqualsBuilder().append(hostname, other.hostname).append(reposName, other.reposName)
                        .isEquals();
            } else {
                return false;
            }
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);
        }

    }
}
