/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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.
 */
/*
 * Copyright (c) 2017, The Linux Foundation.
 */

/*
 * Copyright 2012 Giesecke & Devrient GmbH.
 *
 * 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.android.se.security.gpac;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * REF-AR_DO: The REF-AR-DO contains access rules inclusively its corresponding references for the
 * SE application (AID reference) and device application (hash reference).
 */
public class REF_AR_DO extends BerTlv {

    public static final int TAG = 0xE2;

    private REF_DO mRefDo = null;
    private AR_DO mArDo = null;

    public REF_AR_DO(byte[] rawData, int valueIndex, int valueLength) {
        super(rawData, TAG, valueIndex, valueLength);
    }

    public REF_AR_DO() {
        super(null, TAG, 0, 0);
    }

    public REF_AR_DO(REF_DO refDo, AR_DO arDo) {
        super(null, TAG, 0, 0);
        mRefDo = refDo;
        mArDo = arDo;
    }

    public REF_DO getRefDo() {
        return mRefDo;
    }

    public AR_DO getArDo() {
        return mArDo;
    }

    /**
     * Interpret data.
     *
     * <p>Tags: E2 Length: n
     *
     * <p>Value: REF-DO | AR-DO: A concatenation of an REF-DO and an AR-DO. The REF-DO must
     * correspond
     * to the succeeding AR-DO.
     *
     * <p>Length: n bytes.
     */
    @Override
    public void interpret() throws ParserException {

        mRefDo = null;
        mArDo = null;

        byte[] data = getRawData();
        int index = getValueIndex();

        if (index + getValueLength() > data.length) {
            throw new ParserException("Not enough data for AR_DO!");
        }

        do {
            BerTlv temp = BerTlv.decode(data, index);
            if (temp.getTag() == REF_DO.TAG) { // REF-DO
                mRefDo = new REF_DO(data, temp.getValueIndex(), temp.getValueLength());
                mRefDo.interpret();
            } else if (temp.getTag() == AR_DO.TAG) { // AR-DO
                mArDo = new AR_DO(data, temp.getValueIndex(), temp.getValueLength());
                mArDo.interpret();
            } else {

                // uncomment following line if a more restrictive
                // behavior is necessary.
                // throw new ParserException("Invalid DO in REF-AR-DO!");
            }
            index = temp.getValueIndex() + temp.getValueLength();
        } while (getValueIndex() + getValueLength() > index);

        // check for Carrier Privilege rules
        if (mRefDo != null && mArDo == null && mRefDo.getAidDo() != null
                && mRefDo.getAidDo().isCarrierPrivilege()) {
            return;
        }

        // check for mandatory TLVs.
        if (mRefDo == null) {
            throw new ParserException("Missing Ref-DO in REF-AR-DO!");
        }
        if (mArDo == null) {
            throw new ParserException("Missing AR-DO in REF-AR-DO!");
        }
    }

    /** Tag: E2 Length: n Value: REF-DO | AR-DO: A concatenation of an REF-DO and an AR-DO. */
    @Override
    public void build(ByteArrayOutputStream stream) throws DO_Exception {
        ByteArrayOutputStream temp = new ByteArrayOutputStream();

        if (mRefDo == null || mArDo == null) {
            throw new DO_Exception("REF-AR-DO: Required DO missing!");
        }
        stream.write(getTag());

        mRefDo.build(temp);
        mArDo.build(temp);

        byte[] data = temp.toByteArray();
        BerTlv.encodeLength(data.length, stream);
        try {
            stream.write(data);
        } catch (IOException e) {
            throw new DO_Exception("REF-AR-DO Memory IO problem! " + e.getMessage());
        }
    }
}
