using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Rglib.Interop;

namespace rglib.libtest {
    public partial class DeviceInteractionForm : Form {
        /// <summary>
        /// Используемая считка
        /// </summary>
        private Reader _reader;

        private BindingList<CardFamilyProxy> _cardFamilies;
        private BindingList<MifareProfile> _defaultProfiles;
        private BindingList<Codogramm> _defaultCodogramms;
        private BindingList<CodogrammPriorityProxy> _codogramPriorities;

        public DeviceInteractionForm() : this(null) {
        }

        public DeviceInteractionForm(Reader reader) : base() {
            InitializeComponent();
            _reader = reader;
            _cardFamilies = new BindingList<CardFamilyProxy>();
            cardFamiliesCheckdListBox.DataSource = _cardFamilies;

            _defaultProfiles = new BindingList<MifareProfile>();
            profilesListBox.DataSource = _defaultProfiles;

            _defaultCodogramms = new BindingList<Codogramm>();
            codogramsListBox.DataSource = _defaultCodogramms;

            soundBox.BindingContext = new BindingContext();
            soundBox.DataSource = _defaultCodogramms;

            redBox.BindingContext = new BindingContext();
            redBox.DataSource = _defaultCodogramms;

            greenBox.BindingContext = new BindingContext();
            greenBox.DataSource = _defaultCodogramms;

            blueBox.BindingContext = new BindingContext();
            blueBox.DataSource = _defaultCodogramms;

            _codogramPriorities = new BindingList<CodogrammPriorityProxy>();
            //priotiryBox.BindingContext = new BindingContext();
            priotiryBox.DataSource = _codogramPriorities;
        }

        private void UpdateDeviceInformation() {
            connectionTypeLabel.Text = ConnectionTypeToString(_reader.Endpoint.Type);
            connectionAddressLabel.Text = _reader.Endpoint.Address;
            deviceTypeLabel.Text = _reader.Name;
            deviceAddressLabel.Text = _reader.Address.ToString("D1");
            serialNumberLabel.Text = _reader.SerialNumber.ToString("X8");
            firmwareVersionLabel.Text = _reader.FirmwareVersion;

            hidCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_HID_EM);
            temicCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_TEMIC);
            indalaCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_COTAG);
            mifareCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_MIFARE);
            infCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_INF);

            nfcCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_NFC);
            nfcPayCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_NFC_PAY);
            bleCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_BLE);
            tmwCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_TM_W);
            rbusCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_RBUS);

            rs485CapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_RS485);
            usbCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_USB);
            memCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_HAS_MEMORY);
            keyboardCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_HAS_KEYBOARD);
            rtcCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_HAS_CLOCK);
            relayCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_HAS_RELAY);
            readerCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_READER);
            controllerCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_CONTROLLER);
            odspCapCheckBox.Checked = _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_ODSP);
        }

        private void UpdateCardFamiliesBindings() {
            _cardFamilies.Clear();
            if (_reader.CheckCapability(_E_RG_CAPABILITIES.CFE_HAS_KEYBOARD)) {
                _cardFamilies.Add(new CardFamilyProxy(RG_CARD_FAMILY_CODE.CFCE_PIN));
            }

            if (_reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_HID_EM)) {
                _cardFamilies.Add(new CardFamilyProxy(RG_CARD_FAMILY_CODE.CFCE_HID));
                _cardFamilies.Add(new CardFamilyProxy(RG_CARD_FAMILY_CODE.CFCE_EM));
            }

            if (_reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_COTAG)) {
                _cardFamilies.Add(new CardFamilyProxy(RG_CARD_FAMILY_CODE.CFCF_INDALA));
                _cardFamilies.Add(new CardFamilyProxy(RG_CARD_FAMILY_CODE.CFCE_COTAG));
            }

            if (_reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_TEMIC)) {
                _cardFamilies.Add(new CardFamilyProxy(RG_CARD_FAMILY_CODE.CFCF_TEMIC));
            }

            if (_reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_MIFARE)) {
                _cardFamilies.Add(new CardFamilyProxy(RG_CARD_FAMILY_CODE.CFCE_MIFARE));
            }

            cardFamiliesCheckdListBox.Refresh();
        }

        private void UpdateCodogramPriorityBindings() {
            _codogramPriorities.Clear();
            _codogramPriorities.Add(new CodogrammPriorityProxy(RG_CODOGRAMM_PRIORITY.CPE_BACKGROUND));
            _codogramPriorities.Add(new CodogrammPriorityProxy(RG_CODOGRAMM_PRIORITY.CPE_CYCLIC_LO));
            _codogramPriorities.Add(new CodogrammPriorityProxy(RG_CODOGRAMM_PRIORITY.CPE_CYCLIC_HI));
            _codogramPriorities.Add(new CodogrammPriorityProxy(RG_CODOGRAMM_PRIORITY.CPE_ONCE_LO));
            _codogramPriorities.Add(new CodogrammPriorityProxy(RG_CODOGRAMM_PRIORITY.CPE_ONCE_HI));

            priotiryBox.Refresh();
        }

        private void UpdateDefaultCodogrammBindings() {
            _defaultCodogramms.Clear();
            for (byte index = 0; index < 5; index++) {
                _defaultCodogramms.Add(new Codogramm() {
                    CodogrammBody = 0x00000000,
                    Name = $"Тестовая {index + 1}",
                    Number = (byte) (index + 1),
                    LengthBits = 32
                });
            }

            codogramsListBox.Refresh();
        }

        private void PrepareDefaultProfiles() {
            _defaultProfiles.Clear();

            foreach (int profileIndex in Enumerable.Range(0, 5)) {
                int profileIndexLocal = profileIndex;
                _defaultProfiles.Add(new MifareProfile() {Name = $"Профиль {profileIndexLocal + 1}"});
            }

            profilesListBox.Refresh();
        }

        private string ConnectionTypeToString(RG_ENDPOINT_TYPE type) {
            switch (type) {
                case RG_ENDPOINT_TYPE.CTE_USBHID:
                    return "USB HID";
                case RG_ENDPOINT_TYPE.CTE_SERIAL:
                    return "SERIAL";
                case RG_ENDPOINT_TYPE.CTE_UNKNOWN:
                default:
                    return "Неизвестно";
            }
        }

        private void OnLoad(object sender, EventArgs e) {
            UpdateDeviceInformation();
            UpdateCardFamiliesBindings();
            if (_reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_MIFARE | _E_RG_CAPABILITIES.CFE_SUPPORT_NFC |
                                        _E_RG_CAPABILITIES.CFE_SUPPORT_NFC_PAY)) {
                PrepareDefaultProfiles();
            }
            else {
                profilesListBox.Enabled = false;
                writeSelectedProfiles.Enabled = false;
                writeAllProfiles.Enabled = false;
                clearAllProfiles.Enabled = false;
                editSelectedProfile.Enabled = false;
                readWriteMifareBlock.Enabled = false;
            }

            UpdateCodogramPriorityBindings();
            UpdateDefaultCodogrammBindings();
        }

        private void checkBox16_CheckedChanged(object sender, EventArgs e) {
        }

        private void EditSelectedProfileButtonClick(object sender, EventArgs e) {
            if (profilesListBox.SelectedIndex >= 0) {
                MifareProfile localData = _defaultProfiles[profilesListBox.SelectedIndex];
                using (ProfilesEditDialog editDialog = new ProfilesEditDialog(localData,
                        _reader.CheckCapability(_E_RG_CAPABILITIES.CFE_SUPPORT_NFC |
                                                _E_RG_CAPABILITIES.CFE_SUPPORT_NFC_PAY))
                    {StartPosition = FormStartPosition.CenterParent}) {
                    if (editDialog.ShowDialog(this) == DialogResult.OK) {
                        profilesListBox.Refresh();
                    }
                }
            }
        }

        private void EditCodogrammButtonClick(object sender, EventArgs e) {
            Codogramm codogramm = codogramsListBox.SelectedItem as Codogramm;
            if (codogramm != null) {
                using (CodogrammEditDialog editDialog =
                    new CodogrammEditDialog(codogramm) {StartPosition = FormStartPosition.CenterParent}) {
                    if (editDialog.ShowDialog(this) == DialogResult.OK) {
                        codogramsListBox.Refresh();
                        soundBox.ResetDataSource(_defaultCodogramms);
                        redBox.ResetDataSource(_defaultCodogramms);
                        greenBox.ResetDataSource(_defaultCodogramms);
                        blueBox.ResetDataSource(_defaultCodogramms);
                    }
                }
            }
        }

        private void label15_Click(object sender, EventArgs e) {
        }

        private void SetSelectedMaskButtonClick(object sender, EventArgs e) {
            byte mask = 0;
            foreach (object checkedItem in cardFamiliesCheckdListBox.CheckedItems) {
                if (checkedItem is CardFamilyProxy) {
                    mask |= (checkedItem as CardFamilyProxy).Code;
                }
            }

            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;

                uint errorCode = Methods.RG_SetCardsMask(ref portEndpoin, _reader.Address, (RG_CARD_FAMILY_CODE) mask);
                if (errorCode != 0) {
                    throw new InvalidOperationException(
                        $"Ошибка при установке маски карт. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }

            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void SetAllCardsMaskButtonClick(object sender, EventArgs e) {
            _authorizeCheckBehavior = true;
            for (int index = 0; index < cardFamiliesCheckdListBox.Items.Count; index++) {
                cardFamiliesCheckdListBox.SetItemCheckState(index, CheckState.Checked);
            }

            _authorizeCheckBehavior = false;

            byte mask = 0;
            foreach (var checkedItem in cardFamiliesCheckdListBox.CheckedItems) {
                mask |= (checkedItem as CardFamilyProxy).Code;
            }

            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;

                uint errorCode = Methods.RG_SetCardsMask(ref portEndpoin, _reader.Address, (RG_CARD_FAMILY_CODE) mask);
                if (errorCode != 0) {
                    throw new InvalidOperationException(
                        $"Ошибка при установке маски карт. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }

            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void ResetCardsMaskButtonClick(object sender, EventArgs e) {
            _authorizeCheckBehavior = true;
            for (int index = 0; index < cardFamiliesCheckdListBox.Items.Count; index++) {
                cardFamiliesCheckdListBox.SetItemCheckState(index, CheckState.Unchecked);
            }

            _authorizeCheckBehavior = false;

            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;
                uint errorCode = Methods.RG_SetCardsMask(ref portEndpoin, _reader.Address, 0);
                if (errorCode != 0) {
                    throw new InvalidOperationException(
                        $"Ошибка при установке маски карт. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }

            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void WriteSelectedProfilesButtonClick(object sender, EventArgs e) {
            WriteSelectedProfiles();
        }

        private void WriteAllProfilesButtonClick(object sender, EventArgs e) {
            _authorizeCheckBehavior = true;
            for (int index = 0; index < profilesListBox.Items.Count; index++) {
                profilesListBox.SetItemCheckState(index, CheckState.Checked);
            }
            _authorizeCheckBehavior = false;

            WriteSelectedProfiles();
        }

        private void WriteSelectedProfiles()
        {
            try {
                if (profilesListBox.CheckedItems.Count > 0) {
                    RG_ENDPOINT portEndpoin = _reader.Endpoint;

                    uint errorCode = Methods.RG_ClearProfiles(ref portEndpoin, _reader.Address);
                    if (errorCode != 0) {
                        throw new InvalidOperationException(
                            $"Ошибка при очистке профилей. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                    }

                    RG_CARD_AUTH_PARAMS apiProfileData = new RG_CARD_AUTH_PARAMS();
                    apiProfileData.Flags = 0;
                    apiProfileData.ClassicKey = new byte[6];
                    apiProfileData.PlusKey = new byte[16];

                    byte profileIndex = 0;
                    byte[] accesFlagsStorage = new byte[1];
                    foreach (var checkedItem in profilesListBox.CheckedItems)
                    {
                        if (checkedItem is MifareProfile profile)
                        {
                            apiProfileData.Flags = profile.FillAccessFlags();
                            Buffer.BlockCopy(profile.ClassicKey, 0, apiProfileData.ClassicKey, 0,
                                apiProfileData.ClassicKey.Length);
                            Buffer.BlockCopy(profile.PlusKey, 0, apiProfileData.PlusKey, 0,
                                apiProfileData.PlusKey.Length);

                            errorCode = Methods.RG_WriteProfile(ref portEndpoin, _reader.Address, profileIndex++, profile.BlockNumber, ref apiProfileData);
                            if (errorCode != 0)
                            {
                                throw new InvalidOperationException(
                                    $"Ошибка при записи профилей. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                            }
                        }
                    }
                }
            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void ClearAllProfilesButtonClick(object sender, EventArgs e) {
            _authorizeCheckBehavior = true;
            for (int index = 0; index < profilesListBox.Items.Count; index++)
            {
                profilesListBox.SetItemCheckState(index, CheckState.Unchecked);
            }
            _authorizeCheckBehavior = false;


            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;

                uint errorCode = Methods.RG_ClearProfiles(ref portEndpoin, _reader.Address);
                if (errorCode != 0) {
                    throw new InvalidOperationException(
                        $"Ошибка при очистке профилей. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }
            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void WriteSelectedCodogrammButtonClick(object sender, EventArgs e)
        {
            WriteSelectedCodogramms();
        }

        private void WriteAllCodogrammButtonClick(object sender, EventArgs e) {
            _authorizeCheckBehavior = true;
            for (int index = 0; index < codogramsListBox.Items.Count; index++) {
                codogramsListBox.SetItemCheckState(index, CheckState.Checked);
            }

            WriteSelectedCodogramms();
        }

        private void WriteSelectedCodogramms()
        {
            try {
                if (codogramsListBox.CheckedItems.Count > 0) {
                    RG_ENDPOINT portEndpoin = _reader.Endpoint;
                    RG_CODOGRAMM apiCodogramm = new RG_CODOGRAMM();

                    foreach (var checkedItem in codogramsListBox.CheckedItems) {

                        if (checkedItem is Codogramm codogramm)
                        {

                            apiCodogramm.Length = codogramm.LengthBits;
                            apiCodogramm.Body = codogramm.CodogrammBody;

                            var errorCode = Methods.RG_WriteCodogramm(ref portEndpoin, _reader.Address,
                                codogramm.Number,
                                ref apiCodogramm);
                            if (errorCode != 0)
                            {
                                throw new InvalidOperationException(
                                    $"Ошибка при записи кодограммы {codogramm.Number} \"{codogramm.Name}\". {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                            }
                        }
                    }
                }
            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        private void StartIndicationButtonClick(object sender, EventArgs e) {
            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;

                uint errorCode = Methods.RG_StartCodogramm(
                    ref portEndpoin,
                    _reader.Address,
                    Convert.ToByte((priotiryBox.SelectedItem as CodogrammPriorityProxy)?.PriorityValue ?? 0),
                    enableSound.Checked ? _defaultCodogramms[soundBox.SelectedIndex].Number : (byte) 0,
                    enableRed.Checked ? _defaultCodogramms[redBox.SelectedIndex].Number : (byte) 0,
                    enableGreen.Checked ? _defaultCodogramms[greenBox.SelectedIndex].Number : (byte) 0,
                    enableBlue.Checked ? _defaultCodogramms[blueBox.SelectedIndex].Number : (byte) 0
                );

                if (errorCode != 0) {
                    throw new InvalidOperationException(
                        $"Ошибка при запуске индикации. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }

                //MessageBox.Show(this, "Команда выполнена успешно.", "Сообщение", MessageBoxButtons.OK,
                //    MessageBoxIcon.Information);
            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void ControlOutSetButtonClick(object sender, EventArgs e) {
            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;

                uint errorCode = Methods.RG_SetControlOutputState(
                    ref portEndpoin,
                    _reader.Address,
                    Convert.ToByte(controlOutNum.Value),
                    toGround.Checked ? true : false,
                    Convert.ToByte(controlOutTime.Value));

                if (errorCode != 0) {
                    throw new InvalidOperationException(
                        $"Ошибка при установке состояния управляющего выхода. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }

            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void GetStatusButtonClick(object sender, EventArgs e) {
            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;


                byte pinStates = 0;
                RG_STATUS_TYPE statusType = RG_STATUS_TYPE.STE_UNKNOWN;
                RG_CARD_INFO cardInfo = new RG_CARD_INFO();
                RG_CARD_MEMORY cardMemory = new RG_CARD_MEMORY();
                byte[] blockMemory = new byte[16];

                uint errorCode = Methods.RG_GetStatus(ref portEndpoin, _reader.Address, ref statusType, ref pinStates,
                    ref cardInfo, ref cardMemory);
                if (errorCode != 0 && statusType == RG_STATUS_TYPE.STE_UNKNOWN) {
                    throw new InvalidOperationException(
                        $"Ошибка при запросе статуса сутройства. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }

                switch (statusType) {
                    case RG_STATUS_TYPE.STE_NO_CARD: {
                        cardCodeTextBox.Text = "";
                        cardTypeTextBox.Text = "";
                        cardUidTextBox.Text = "";
                        profileNumBox.Text = "";
                        cardMemoryTextBox.Text = "";
                        break;
                    }
                    case RG_STATUS_TYPE.STE_CARD: {
                        string enumFieldName = Enum.GetName(typeof(RG_CARD_TYPE_CODE), cardInfo.Type);
                        string uidField = BitConverter.ToString(cardInfo.Uid);

                        cardCodeTextBox.Text = ((byte) cardInfo.Type).ToString("X2");
                        cardTypeTextBox.Text = enumFieldName;
                        cardUidTextBox.Text = uidField;
                        profileNumBox.Text = "";
                        cardMemoryTextBox.Text = "";
                        break;
                    }
                    case RG_STATUS_TYPE.STE_CARD_AUTH: {
                        Buffer.BlockCopy(cardMemory.BlockData, 0, blockMemory, 0,
                            Math.Min(cardMemory.BlockData.Length, blockMemory.Length));
                        string enumFieldName = Enum.GetName(typeof(RG_CARD_TYPE_CODE), cardInfo.Type);
                        string uidField = BitConverter.ToString(cardInfo.Uid);
                        string memoryData = BitConverter.ToString(blockMemory);

                        cardCodeTextBox.Text = ((byte) cardInfo.Type).ToString("X2");
                        cardTypeTextBox.Text = enumFieldName;
                        cardUidTextBox.Text = uidField;
                        profileNumBox.Text = cardMemory.ProfileOrBlockNum.ToString();
                        cardMemoryTextBox.Text = memoryData;

                        break;
                    }
                    case RG_STATUS_TYPE.STE_CARD_NO_AUTH: {
                        string enumFieldName = Enum.GetName(typeof(RG_CARD_TYPE_CODE), cardInfo.Type);
                        string uidField = BitConverter.ToString(cardInfo.Uid);

                        cardCodeTextBox.Text = ((byte) cardInfo.Type).ToString("X2");
                        cardTypeTextBox.Text = enumFieldName;
                        cardUidTextBox.Text = uidField;
                        profileNumBox.Text = "";
                        cardMemoryTextBox.Text = $"Ошибка доступа 0x{cardMemory.ProfileOrBlockNum:X2}";


                        break;
                    }
                }
            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
        private void InitializeButtonClick(object sender, EventArgs e) {
            try {
                RG_ENDPOINT portEndpoin = _reader.Endpoint;

                uint errorCode = Methods.RG_InitDevice(
                    ref portEndpoin,
                    _reader.Address);

                if (errorCode != 0) {
                    throw new InvalidOperationException(
                        $"Ошибка при выполнении инициализации устройства. {errorCode} {ApiErrorHelper.GetErrorDescription(errorCode)}");
                }

            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private bool _authorizeCheckBehavior;

        private void DoCheck(object sender, ItemCheckEventArgs e) {
            if (!_authorizeCheckBehavior)
                e.NewValue = e.CurrentValue;
        }

        private void DoCheckDown(object sender, MouseEventArgs e) {
            CheckedListBox targetBox = sender as CheckedListBox;
            Point loc = targetBox.PointToClient(Cursor.Position);
            for (int i = 0; i < targetBox.Items.Count; i++) {
                Rectangle rec = targetBox.GetItemRectangle(i);
                rec.Width = 16;

                if (rec.Contains(loc)) {
                    _authorizeCheckBehavior = true;
                    bool newValue = !targetBox.GetItemChecked(i);
                    targetBox.SetItemChecked(i, newValue);
                    _authorizeCheckBehavior = false;

                    return;
                }
            }
        }

        private void RwButtonClick(object sender, EventArgs e) {
            using (var frm = new BlockReadWriteDialog(_reader)) {
                frm.StartPosition = FormStartPosition.CenterParent;
                frm.ShowDialog(this);
            }
        }

        private void OnClosing(object sender, FormClosingEventArgs e) {
            if (_reader != null) {
                try {
                    RG_ENDPOINT endpoint = _reader.Endpoint;
                    Methods.RG_CloseDevice(ref endpoint, _reader.Address);
                }
                catch {
                    // ignore
                }
            }
        }

        private void isoCommandsButton_Click(object sender, EventArgs e)
        {
            using (IsoExchangeForm isoDialog = new IsoExchangeForm(_reader) {StartPosition = FormStartPosition.CenterParent}) {
                isoDialog.ShowDialog(this);
            }
        }
    }

    public static class ControlExtension {
        public static void InvokeIfRequired(this ISynchronizeInvoke obj, MethodInvoker action) {
            if (obj.InvokeRequired) {
                var args = new object[0];
                obj.Invoke(action, args);
            }
            else {
                action();
            }
        }
    }

    public static class DataSourceExtensions {
        public static void ResetDataSource(this ListControl target, object newSource) {
            target.DataSource = null;
            target.DataSource = newSource;
        }
    }
}