#include "key_writer.h"
#include "key_commands.h"

KeyWriter::KeyWriter(OneWireMaster* _onewire_master) {
    onewire_master = _onewire_master;
}

KeyWriter::~KeyWriter() {
    stop();
}

KeyWriter::Error KeyWriter::write(iButtonKey* key) {
    return write_internal(key);
}

void KeyWriter::start() {
    furi_hal_power_enable_otg();
    onewire_master->start();
}

void KeyWriter::stop() {
    furi_hal_power_disable_otg();
    onewire_master->stop();
}

KeyWriter::Error KeyWriter::write_internal(iButtonKey* key) {
    Error result = Error::NO_DETECT;
    bool same_key = false;

    osKernelLock();
    bool presence = onewire_master->reset();
    osKernelUnlock();

    if(presence) {
        switch(key->get_key_type()) {
        case iButtonKeyType::KeyDallas:
            same_key = compare_key_ds1990(key);

            if(!same_key) {
                bool write_result = false;
                // currently we can write:
                // RW1990, TM08v2, TM08vi-2 by write_1990_1()
                // RW2004, RW2004 with EEPROM by write_TM2004();

                if(!write_result) {
                    write_result = write_1990_1(key);
                }
                if(!write_result) {
                    write_result = write_1990_2(key);
                }
                if(!write_result) {
                    write_result = write_TM2004(key);
                }

                if(write_result) {
                    result = Error::OK;
                } else {
                    result = Error::CANNOT_WRITE;
                }
            } else {
                result = Error::SAME_KEY;
            }
            break;

        default:
            break;
        }
    }

    return result;
}

bool KeyWriter::compare_key_ds1990(iButtonKey* key) {
    bool result = false;

    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
        FURI_CRITICAL_ENTER();
        bool presence = onewire_master->reset();

        if(presence) {
            onewire_master->write(DS1990::CMD_READ_ROM);

            result = true;
            for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
                if(key->get_data()[i] != onewire_master->read()) {
                    result = false;
                    break;
                }
            }
        }

        FURI_CRITICAL_EXIT();
    }

    return result;
}

bool KeyWriter::write_1990_1(iButtonKey* key) {
    bool result = false;

    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
        FURI_CRITICAL_ENTER();

        // unlock
        onewire_master->reset();
        onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG);
        delay_us(10);
        onewire_write_one_bit(0, 5000);

        // write key
        onewire_master->reset();
        onewire_master->write(RW1990_1::CMD_WRITE_ROM);
        for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
            // inverted key for RW1990.1
            write_byte_ds1990(~key->get_data()[i]);
            delay_us(30000);
        }

        // lock
        onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG);
        onewire_write_one_bit(1);

        FURI_CRITICAL_EXIT();

        if(compare_key_ds1990(key)) {
            result = true;
        }
    }

    return result;
}

bool KeyWriter::write_1990_2(iButtonKey* key) {
    bool result = false;

    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
        FURI_CRITICAL_ENTER();

        // unlock
        onewire_master->reset();
        onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG);
        delay_us(10);
        onewire_write_one_bit(1, 5000);

        // write key
        onewire_master->reset();
        onewire_master->write(RW1990_2::CMD_WRITE_ROM);
        for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
            write_byte_ds1990(key->get_data()[i]);
            delay_us(30000);
        }

        // lock
        onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG);
        onewire_write_one_bit(0);

        FURI_CRITICAL_EXIT();

        if(compare_key_ds1990(key)) {
            result = true;
        }
    }

    return result;
}

bool KeyWriter::write_TM2004(iButtonKey* key) {
    uint8_t answer;
    bool result = true;

    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
        FURI_CRITICAL_ENTER();

        // write rom, addr is 0x0000
        onewire_master->reset();
        onewire_master->write(TM2004::CMD_WRITE_ROM);
        onewire_master->write(0x00);
        onewire_master->write(0x00);

        // write key
        for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
            // write key byte
            onewire_master->write(key->get_data()[i]);
            answer = onewire_master->read();
            // TODO: check answer CRC

            // pulse indicating that data is correct
            delay_us(600);
            onewire_write_one_bit(1, 50000);

            // read writed key byte
            answer = onewire_master->read();

            // check that writed and readed are same
            if(key->get_data()[i] != answer) {
                result = false;
                break;
            }
        }

        if(!compare_key_ds1990(key)) {
            result = false;
        }

        onewire_master->reset();

        FURI_CRITICAL_EXIT();
    } else {
        result = false;
    }

    return result;
}

bool KeyWriter::write_TM01(iButtonKey* key) {
    /*bool result = true;

    // TODO test and encoding
    FURI_CRITICAL_ENTER();

    // unlock
    onewire_master->reset();
    onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG);
    onewire_write_one_bit(1, 10000);

    // write key
    onewire_master->reset();
    onewire_master->write(TM01::CMD_WRITE_ROM);

    // TODO: key types
    //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
    //} else {
    for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
        write_byte_ds1990(key->get_data()[i]);
        delay_us(10000);
    }
    //}

    // lock
    onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG);
    onewire_write_one_bit(0, 10000);

    FURI_CRITICAL_EXIT();

    if(!compare_key_ds1990(key)) {
        result = false;
    }

    FURI_CRITICAL_ENTER();

    if(key->get_key_type() == iButtonKeyType::KeyMetakom ||
       key->get_key_type() == iButtonKeyType::KeyCyfral) {
        onewire_master->reset();
        if(key->get_key_type() == iButtonKeyType::KeyCyfral)
            onewire_master->write(TM01::CMD_SWITCH_TO_CYFRAL);
        else
            onewire_master->write(TM01::CMD_SWITCH_TO_METAKOM);
        onewire_write_one_bit(1);
    }

    FURI_CRITICAL_EXIT();

    return result;*/
    return false;
}

void KeyWriter::onewire_write_one_bit(bool value, uint32_t delay) {
    onewire_master->write_bit(value);
    delay_us(delay);
}

void KeyWriter::write_byte_ds1990(uint8_t data) {
    for(uint8_t n_bit = 0; n_bit < 8; n_bit++) {
        onewire_master->write_bit(data & 1);
        delay_us(5000);
        data = data >> 1;
    }
}
