/*******************************************************************************
 * Copyright 2014 Trevor Robinson
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.scurrilous.circe.crc;

import java.util.EnumSet;
import com.scurrilous.circe.Hash;
import com.scurrilous.circe.HashSupport;
import com.scurrilous.circe.StatelessHash;
import com.scurrilous.circe.impl.AbstractHashProvider;
import com.scurrilous.circe.params.CrcParameters;

/**
 * Provides pure Java and JDK-supplied CRC implementations.
 */
public final class StandardCrcProvider extends AbstractHashProvider<CrcParameters> {

    /**
     * Constructs a new {@link StandardCrcProvider}.
     */
    public StandardCrcProvider() {
        super(CrcParameters.class);
    }

    @Override
    protected EnumSet<HashSupport> querySupportTyped(CrcParameters params) {
        final EnumSet<HashSupport> result = EnumSet.of(HashSupport.STATEFUL,
                HashSupport.INCREMENTAL, HashSupport.STATELESS_INCREMENTAL, HashSupport.LONG_SIZED);
        if (params.bitWidth() <= 32)
            result.add(HashSupport.INT_SIZED);
        if (params.equals(CrcParameters.CRC32))
            result.add(HashSupport.NATIVE);
        return result;
    }

    @Override
    protected Hash get(CrcParameters params, EnumSet<HashSupport> required) {
        if (!required.contains(HashSupport.STATELESS_INCREMENTAL) &&
                params.equals(CrcParameters.CRC32))
            return new JavaCrc32();
        if (required.contains(HashSupport.NATIVE))
            throw new UnsupportedOperationException();
        return getCacheable(params, required);
    }

    @Override
    protected StatelessHash createCacheable(CrcParameters params, EnumSet<HashSupport> required) {
        final int bitWidth = params.bitWidth();
        if (bitWidth > 32 || (required.contains(HashSupport.LONG_SIZED) && bitWidth >= 8)) {
            if (required.contains(HashSupport.INT_SIZED))
                throw new UnsupportedOperationException();
            if (params.reflected())
                return new ReflectedLongCrc(params.algorithm(), bitWidth, params.polynomial(),
                        params.initial(), params.xorOut());
            else
                return new NormalLongCrc(params.algorithm(), bitWidth, params.polynomial(),
                        params.initial(), params.xorOut());
        } else {
            if (params.reflected())
                return new ReflectedIntCrc(params.algorithm(), bitWidth, (int) params.polynomial(),
                        (int) params.initial(), (int) params.xorOut());
            else if (bitWidth > 8)
                return new NormalIntCrc(params.algorithm(), bitWidth, (int) params.polynomial(),
                        (int) params.initial(), (int) params.xorOut());
            return new NormalByteCrc(params.algorithm(), bitWidth, (int) params.polynomial(),
                    (int) params.initial(), (int) params.xorOut());
        }
    }
}
