package org.bouncycastle.crypto.test.cavp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA224Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.macs.HMac;

public class CAVPReader
{

    private static final Pattern COMMENT_PATTERN = Pattern.compile("^\\s*\\#\\s*(.*)$");
    private static final Pattern CONFIG_PATTERN = Pattern.compile("^\\s*+\\[\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+\\]\\s*+$");
    private static final Pattern VECTOR_PATTERN = Pattern.compile("^\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+$");
    private static final Pattern EMPTY_PATTERN = Pattern.compile("^\\s*+$");
    static final Pattern PATTERN_FOR_R = Pattern.compile("(\\d+)_BITS");
    private final CAVPListener listener;
    private String name;
    private BufferedReader lineReader;


    public CAVPReader(CAVPListener listener)
    {
        this.listener = listener;
    }

    public void setInput(String name, Reader reader)
    {
        this.name = name;
        this.lineReader = new BufferedReader(reader);
    }

    public void readAll()
        throws IOException
    {

        listener.setup();

        Properties config = new Properties();

        boolean startNewVector = true;

        Properties vectors = new Properties();

        while (true)
        {
            final String line = lineReader.readLine();
            if (line == null)
            {
                listener.receiveEnd();
                break;
            }

            final Matcher commentMatcher = COMMENT_PATTERN.matcher(line);
            if (commentMatcher.matches())
            {
                listener.receiveCommentLine(commentMatcher.group(1));
                continue;
            }

            final Matcher configMatcher = CONFIG_PATTERN.matcher(line);
            if (configMatcher.matches())
            {
                config.put(configMatcher.group(1), configMatcher.group(2));
                continue;
            }

            final Matcher vectorMatcher = VECTOR_PATTERN.matcher(line);
            if (vectorMatcher.matches())
            {
                vectors.put(vectorMatcher.group(1), vectorMatcher.group(2));
                startNewVector = false;
                continue;
            }

            final Matcher emptyMatcher = EMPTY_PATTERN.matcher(line);
            if (emptyMatcher.matches())
            {
                if (startNewVector)
                {
                    continue;
                }

                listener.receiveCAVPVectors(name, config, vectors);
                vectors = new Properties();
                startNewVector = true;
            }
        }

        listener.tearDown();
    }

    static Mac createPRF(Properties config)
    {
        final Mac prf;
        if (config.getProperty("PRF").matches("CMAC_AES\\d\\d\\d"))
        {
            BlockCipher blockCipher = AESEngine.newInstance();
            prf = new CMac(blockCipher);
        }
        else if (config.getProperty("PRF").matches("CMAC_TDES\\d"))
        {
            BlockCipher blockCipher = new DESedeEngine();
            prf = new CMac(blockCipher);
        }
        else if (config.getProperty("PRF").matches("HMAC_SHA1"))
        {
            Digest digest = new SHA1Digest();
            prf = new HMac(digest);
        }
        else if (config.getProperty("PRF").matches("HMAC_SHA224"))
        {
            Digest digest = new SHA224Digest();
            prf = new HMac(digest);
        }
        else if (config.getProperty("PRF").matches("HMAC_SHA256"))
        {
            Digest digest = SHA256Digest.newInstance();
            prf = new HMac(digest);
        }
        else if (config.getProperty("PRF").matches("HMAC_SHA384"))
        {
            Digest digest = new SHA384Digest();
            prf = new HMac(digest);
        }
        else if (config.getProperty("PRF").matches("HMAC_SHA512"))
        {
            Digest digest = new SHA512Digest();
            prf = new HMac(digest);
        }
        else
        {
            throw new IllegalStateException("Unknown Mac for PRF");
        }
        return prf;
    }

}
