package org.iri_research.renkan.models;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;
import org.iri_research.renkan.Constants;
import org.iri_research.renkan.RenkanException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonIgnore;

public abstract class AbstractRenkanModel<ID extends Serializable> implements
        IRenkanModel<ID> {

    @SuppressWarnings("unused")
    private static Logger logger = LoggerFactory.getLogger(AbstractRenkanModel.class);
    
    public AbstractRenkanModel(ID id, String title, String description,
            String uri) {
        super();
        this.id = id;
        this.idSet = true;
        this.title = title;
        this.description = description;
        this.uri = uri;
    }

    protected AbstractRenkanModel() {
    }

    protected ID id = this.getDefaultId();
    @JsonIgnore
    protected boolean idSet = false;
    protected String title;
    protected String description;
    protected String uri;

    @Override
    public String getTitle() {
        return this.title;
    }

    @Override
    public String getDescription() {
        return this.description;
    }

    @Override
    public String getUri() {
        return this.uri;
    }

    @Override
    public ID getId() {
        return this.id;
    }

    @Override
    public void setId(ID id) {
        this.idSet = true;
        this.id = id;
    }
    
    @JsonIgnore
    @Override
    public boolean isIdSet() {
        return this.idSet;
    };

    @Override
    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public void setUri(String uri) {
        this.uri = uri;
    }

    abstract protected String getRawKeyPart();
    abstract protected ID getDefaultId();

    private String getRawKey(String salt) {
        StringBuffer key = new StringBuffer(salt != null ? salt + "|" : "");
        key.append(this.getId());
        key.append('|');
        key.append(this.getRawKeyPart());
        return key.toString();
    }

    public String getKey(String salt) throws RenkanException {

        String rawKey = this.getRawKey(salt);

        MessageDigest md;
        try {
            md = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            throw new RenkanException("NoSuchAlgorithmException digest: "
                    + e.getMessage(), e);
        }
        String key;
        final SecretKeySpec secret_key = new SecretKeySpec(
                Constants.KEYHEX.getBytes(), "HmacSHA256");
        md.update(secret_key.getEncoded());
        try {
            key = Hex.encodeHexString(md.digest(rawKey.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException e) {
            throw new RenkanException("UnsupportedEncodingException digest: "
                    + e.getMessage(), e);
        }

        return key;
    }

    public boolean checkKey(String key, String salt) throws RenkanException {

        if (key == null || key.isEmpty()) {
            return false;
        }

        String signature = key;

        String new_key = this.getKey(salt);

        return new_key.equals(signature);
    }

}
