/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyrighted [year] [name of copyright owner]".
 *
 * Copyright 2014-2015 ForgeRock AS.
 */

package org.forgerock.openam.sts.user.invocation;

import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.ResourceException;
import org.forgerock.openam.sts.AMSTSConstants;
import org.forgerock.openam.sts.TokenMarshalException;
import org.forgerock.openam.sts.TokenType;
import org.forgerock.openam.sts.token.model.RestUsernameToken;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import static org.forgerock.json.JsonValue.field;
import static org.forgerock.json.JsonValue.json;
import static org.forgerock.json.JsonValue.object;

/**
 * Contains state corresponding to a UsernameToken and will emit json which includes state necessary to act as the
 * input_token_state in the RestSTSTokenTranslationInvocationState - i.e. the username and password state, and a
 * AMSTSConstants.TOKEN_TYPE_KEY field corresponding to TokenType.USERNAME.name().
 */
public class UsernameTokenState {
    public static class UsernameTokenStateBuilder {
        private byte[] username;
        private byte[] password;

        private UsernameTokenStateBuilder() {}

        public UsernameTokenStateBuilder username(byte[] username) {
            this.username = username;
            return this;
        }

        public UsernameTokenStateBuilder password(byte[] password) {
            this.password = password;
            return this;
        }

        public UsernameTokenState build() throws TokenMarshalException  {
            return new UsernameTokenState(this);
        }
    }
    private final RestUsernameToken restUsernameToken;

    private UsernameTokenState(UsernameTokenStateBuilder builder) throws TokenMarshalException {
        if (builder.username == null) {
            throw new TokenMarshalException(ResourceException.BAD_REQUEST, "Username must be specified.");
        }
        if (builder.password == null) {
            throw new TokenMarshalException(ResourceException.BAD_REQUEST, "Password must be specified.");
        }
        restUsernameToken = new RestUsernameToken(builder.username, builder.password);
    }

    public static UsernameTokenStateBuilder builder() {
        return new UsernameTokenStateBuilder();
    }

    public byte[] getUsername() {
        return restUsernameToken.getUsername();
    }

    public byte[] getPassword() {
        return restUsernameToken.getPassword();
    }

    public JsonValue toJson() throws TokenMarshalException {
        try {
            return json(object(
                    field(AMSTSConstants.TOKEN_TYPE_KEY, TokenType.USERNAME.name()),
                    field(AMSTSConstants.USERNAME_TOKEN_USERNAME, new String(restUsernameToken.getUsername(), AMSTSConstants.UTF_8_CHARSET_ID)),
                    field(AMSTSConstants.USERNAME_TOKEN_PASSWORD, new String(restUsernameToken.getPassword(), AMSTSConstants.UTF_8_CHARSET_ID))));
        } catch (UnsupportedEncodingException e) {
            throw new TokenMarshalException(ResourceException.BAD_REQUEST, "Unsupported charset marshalling toJson: " + e);
        }
    }

    public static UsernameTokenState fromJson(JsonValue jsonValue) throws TokenMarshalException{
        try {
            return UsernameTokenState.builder()
                    .password(jsonValue.get(AMSTSConstants.USERNAME_TOKEN_PASSWORD).asString().getBytes(AMSTSConstants.UTF_8_CHARSET_ID))
                    .username(jsonValue.get(AMSTSConstants.USERNAME_TOKEN_USERNAME).asString().getBytes(AMSTSConstants.UTF_8_CHARSET_ID))
                    .build();
        } catch (UnsupportedEncodingException e) {
            throw new TokenMarshalException(ResourceException.BAD_REQUEST, "Unsupported charset marshalling fromJson: " + e);
        }
    }

    @Override
    public String toString() {
        try {
            return toJson().toString();
        } catch (TokenMarshalException e) {
            return "Exception caught marshalling toJson: " + e;
        }
    }

    @Override
    public int hashCode() {
        return toString().hashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof UsernameTokenState) {
            UsernameTokenState otherState = (UsernameTokenState)other;
            return Arrays.equals(restUsernameToken.getUsername(), otherState.getUsername()) &&
                    Arrays.equals(restUsernameToken.getPassword(), otherState.getPassword());
        }
        return false;
    }
}
