package com.atlassian.bitbucket.web.conditions;

import com.atlassian.http.mime.UserAgentUtil;
import com.atlassian.http.mime.UserAgentUtilImpl;
import com.atlassian.plugin.PluginParseException;
import com.atlassian.plugin.web.Condition;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Map;

import static com.atlassian.http.mime.UserAgentUtil.*;
import static com.atlassian.http.mime.UserAgentUtil.OperatingSystem.OperatingSystemFamily;

/**
 * A condition which checks the browser user agent.
 *
 * <code>
 * <pre>
 * &lt;condition class="com.atlassian.bitbucket.web.conditions.MatchesUserAgentCondition">
 *     &lt;param name="operatingSystems">MAC&lt;/param>
 * &lt;/condition>
 * </pre>
 * </code>
 *
 * Valid parameters are:
 *
 * <ul>
 *     <li>{@code browsers}</li>
 *     <ul>
 *          <li>{@code MSIE}</li>
 *          <li>{@code FIREFOX}</li>
 *          <li>{@code CHROME}</li>
 *          <li>{@code UKNOWN}</li>
 *     </ul>
 *     <li>{@code browserVersions}</li>
 *     <li>{@code operatingSystems}</li>
 *     <ul>
 *          <li>{@code WINDOWS}</li>
 *          <li>{@code LINUX}</li>
 *          <li>{@code MAC}</li>
 *          <li>{@code BSD}</li>
 *     </ul>
 * </ul>
 */
public class MatchesUserAgentCondition implements Condition {

    private final UserAgentUtil userAgentUtil;

    private EnumSet<BrowserFamily> browserFamilies = EnumSet.allOf(BrowserFamily.class);
    private EnumSet<BrowserMajorVersion> browserMajorVersions = EnumSet.allOf(BrowserMajorVersion.class);
    private EnumSet<OperatingSystemFamily> operatingSystemFamilies = EnumSet.allOf(OperatingSystemFamily.class);

    public MatchesUserAgentCondition() {
        this.userAgentUtil = new UserAgentUtilImpl();
    }

    @Override
    public void init(Map<String, String> params) throws PluginParseException {
        if (params.containsKey("browsers")) {
            browserFamilies = createEnumSet(params.get("browsers"), BrowserFamily.class);
        }
        if (params.containsKey("browserVersions")) {
            browserMajorVersions = createEnumSet(params.get("browserVersions"), BrowserMajorVersion.class);
        }
        if (params.containsKey("operatingSystems")) {
            operatingSystemFamilies = createEnumSet(params.get("operatingSystems"), OperatingSystemFamily.class);
        }
    }

    private <T extends Enum<T>> EnumSet<T> createEnumSet(String value, Class<T> enumClass) throws PluginParseException {
        final String[] values = value.split(",");
        final ArrayList<T> list = new ArrayList<>(values.length);
        for (String s : values) {
            try {
                list.add(Enum.valueOf(enumClass, s));
            } catch (IllegalArgumentException e) {
                throw new PluginParseException(e);
            }
        }
        return EnumSet.copyOf(list);
    }

    @Override
    public boolean shouldDisplay(Map<String, Object> context) {
        final Object request = context.get("request");
        return request instanceof HttpServletRequest && matchesUserAgent(((HttpServletRequest) request).getHeader("user-agent"));
    }

    private boolean matchesUserAgent(String userAgentString) {
        final UserAgent userAgent = userAgentUtil.getUserAgentInfo(userAgentString);
        final Browser browser = userAgent.getBrowser();
        final OperatingSystem operatingSystem = userAgent.getOperatingSystem();
        return browserFamilies.contains(browser.getBrowserFamily()) &&
                browserMajorVersions.contains(browser.getBrowserMajorVersion()) &&
                operatingSystemFamilies.contains(operatingSystem.getOperatingSystemFamily());
    }
}
