#include "test_common.h"

int
main(int argc, char *argv[])
{
    CK_RV                  rc;
    CK_FLAGS               flags = 0;
    CK_SESSION_HANDLE      sess;
    CK_ULONG               i;
    static CK_OBJECT_CLASS oclass_data = CKO_DATA;
    static CK_BBOOL        ltrue       = CK_TRUE;
    static CK_BBOOL        lfalse      = CK_FALSE;
    CK_ATTRIBUTE           data_find[] = {
        { CKA_CLASS, &oclass_data, sizeof(oclass_data) },
    };
    static CK_UTF8CHAR            *label = (CK_UTF8CHAR *)"data";
	CK_ULONG label_len = (CK_ULONG)strlen((char *)label) + 1;
    static CK_UTF8CHAR                *app = (CK_UTF8CHAR *)"pkcs11_library";
	CK_ULONG app_len = (CK_ULONG)strlen((char *)app) + 1;
    static CK_BYTE                data_value[] = {
        0x66, 0x6F, 0x40, 0x63, 0x72, 0x79, 0x70, 0x74,
        0x6F, 0x70, 0x72, 0x6F, 0x2E, 0x72, 0x75, 0x31,
        0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
        0x13, 0x02, 0x52, 0x55, 0x31, 0x13, 0x30, 0x11,
    };
    CK_ATTRIBUTE           data_create[] = {
        { CKA_CLASS,       &oclass_data, sizeof(oclass_data) },
        { CKA_APPLICATION, NULL,			0   },	// 1
        { CKA_TOKEN,       &ltrue,       sizeof(ltrue)       },
        { CKA_PRIVATE,     &lfalse,      sizeof(lfalse)      },
        { CKA_LABEL,       NULL,			0   },	//4
        { CKA_VALUE,       data_value,   sizeof(data_value)  },
    };
    static CK_BYTE                get_value[1024];
    CK_ATTRIBUTE           tmpl_value[] = {
        { CKA_VALUE, get_value, sizeof(get_value) },
    };
    CK_ATTRIBUTE           data_value_find[] = {
        { CKA_CLASS, &oclass_data, sizeof(oclass_data) },
        { CKA_VALUE, data_value,   sizeof(data_value)  },
    };
    CK_ULONG               count, number;
    CK_OBJECT_HANDLE       hObj    = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       hNewObj = CK_INVALID_HANDLE;

	data_create[1].pValue = app;
	data_create[1].ulValueLen = app_len;
	data_create[4].pValue = label;
	data_create[4].ulValueLen = label_len;

    printf("CKO_DATA test\n");

    for (i = 1; i < (CK_ULONG) argc; i++)
    {
        if (strcmp("-api", argv[i]) == 0)
        {
            ++i;
            api_path = argv[i];
        }
        else if (strcmp("-user_pin", argv[i]) == 0)
        {
            ++i;
            user_pin = (CK_UTF8CHAR *)argv[i];
        }
    }
    rc = init(api_path, &funcs, NULL);
    if (rc != CKR_OK)
    {
        printf("init failed\n");
        return rc;
    }
    rc = get_slot_list(funcs,
                       &SlotList,
                       &SlotCount);
    if (rc != CKR_OK)
    {
        printf("get_slot_list failed, rc = 0x%lx\n", rc);
        return rc;
    }
    rc = find_slot_with_token(funcs,
                              SlotList,
                              SlotCount,
                              &SlotId);
    if (rc != CKR_OK)
    {
        printf("find_slot_with_token failed, rc = 0x%lx\n", rc);
        return rc;
    }

    flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
    rc    = funcs->C_OpenSession(SlotId, flags, NULL, NULL, &sess);
    if (rc != CKR_OK)
    {
        printf("C_OpenSession failed, rc = 0x%lx\n", rc);
        return rc;
    }

    rc = funcs->C_Login(sess, CKU_USER, user_pin, (CK_ULONG)strlen((char *)user_pin));
    if (rc != CKR_OK)
    {
        printf("C_Login failed, rc = 0x%lx\n", rc);
        return rc;
    }
    printf("C_Login (CKU_USER) success\n");

    // Ищем все доступные объекты CKO_DATA
    rc = funcs->C_FindObjectsInit(sess,
                                  data_find, sizeof(data_find) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        printf("C_FindObjectsFinal failed, rc = 0x%lx\n", rc);
        return rc;
    }
    count = 0;
    do
    {
        number = 0;
        hObj   = CK_INVALID_HANDLE;
        rc     = funcs->C_FindObjects(sess, &hObj, 1, &number);
        if (rc != CKR_OK)
        {
            printf("C_FindObjects failed, rc = 0x%lx\n", rc);
            return rc;
        }
        if (number > 0 && hObj != CK_INVALID_HANDLE)
            count++;
    } while (number > 0 && hObj != CK_INVALID_HANDLE);
    rc = funcs->C_FindObjectsFinal(sess);
    if (rc != CKR_OK)
    {
        printf("C_FindObjectsFinal failed, rc = 0x%lx\n", rc);
        return rc;
    }
    printf("%ld data objects found for session\n", count);

    rc = funcs->C_CreateObject(sess,
                               data_create, sizeof(data_create) / sizeof(CK_ATTRIBUTE),
                               &hNewObj);
    if (rc != CKR_OK)
    {
        printf("C_CreateObject failed, rc = 0x%lx\n", rc);
        return rc;
    }
    printf("C_CreateObject success\n");

    rc = funcs->C_GetAttributeValue(sess, hNewObj,
                                    tmpl_value, sizeof(tmpl_value) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        printf("C_GetAttributeValue failed, rc = 0x%lx\n", rc);
        return rc;
    }
    if (tmpl_value[0].ulValueLen != sizeof(data_value))
    {
        printf("Invalid CKA_VALUE length\n");
        return -1;
    }
    if (memcmp(get_value, data_value, sizeof(data_value)) != 0)
    {
        printf("Invalid CKA_VALUE\n");
        return -1;
    }
    printf("C_GetAttributeValue success\n");

    // Ищем все доступные объекты CKO_DATA по значению CKA_VALUE
    rc = funcs->C_FindObjectsInit(sess,
                                  data_value_find,
                                  sizeof(data_value_find) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        printf("C_FindObjectsFinal failed, rc = 0x%lx\n", rc);
        return rc;
    }
    count = 0;
    do
    {
        number = 0;
        hObj   = CK_INVALID_HANDLE;
        rc     = funcs->C_FindObjects(sess, &hObj, 1, &number);
        if (rc != CKR_OK)
        {
            printf("C_FindObjects failed, rc = 0x%lx\n", rc);
            return rc;
        }
        if (number > 0 && hObj != CK_INVALID_HANDLE)
            count++;
    } while (number > 0 && hObj != CK_INVALID_HANDLE);
    rc = funcs->C_FindObjectsFinal(sess);
    if (rc != CKR_OK)
    {
        printf("C_FindObjectsFinal failed, rc = 0x%lx\n", rc);
        return rc;
    }
    printf("%ld data objects found for given CKA_VALUE\n",
           count);

    rc = funcs->C_DestroyObject(sess, hNewObj);
    if (rc != CKR_OK)
    {
        printf("C_DestroyObject failed, rc = 0x%lx\n", rc);
        return rc;
    }
    printf("C_DestroyObject success\n");

    rc = funcs->C_CloseSession(sess);
    if (rc != CKR_OK)
    {
        printf("C_CloseSession failed, rc = 0x%lx\n", rc);
        return rc;
    }

    printf("CKO_DATA test SUCCESS\n");
    return CKR_OK;
}
