try {
		conv();
    }

    catch (error &e) {
        std::cerr << "status: " << e.status << std::endl;
        std::cerr << "message: " << e.message << std::endl;
    }

#include <chrono>
#include <iostream>
#include <fstream>
#include <numeric>
#include <math.h>
#include <string>
#include <time.h>
#include "mkldnn.hpp"
#include "configure.h"

using namespace mkldnn;
using namespace std;

void conv()
{
    srand(1);
    std::vector<double> duration_vector;
    double start, end;

    engine cpu_engine(engine::kind::cpu, 0);
    stream cpu_stream(cpu_engine);

    std::vector<primitive> net;
    std::vector<std::unordered_map<int, memory>> net_args;

    // Initialize user buffers
    memory::dims conv_strides = {1, 1};
    memory::dims conv_padding = {0, 0};

    std::vector<float> input_buf(BATCH_SIZE*FIn*(N + 2)*(N + 2));

    std::vector<float> conv_bias_buf(FOut);
    std::vector<float> conv_weights_buf(FOut*FIn*K*K);

    for (int i = 0; i < BATCH_SIZE*FIn*(N + 2)*(N + 2); i++)
        input_buf[i] = ((float)(rand()%256 - 128)) / 127.f;

    for (int i = 0; i < FOut*FIn*K*K; i++)
        conv_weights_buf[i] = ((float)(rand()%256 - 128)) / 127.f;

    for (int i = 0; i < FOut; i++)
        conv_bias_buf[i] = ((float)(rand()%256 - 128)) / 127.f;

    // Create memory objects with user data format
    auto input_usr_md = memory::desc(
        {BATCH_SIZE, FIn, N + 2, N + 2},
        memory::data_type::f32,
        memory::format_tag::nchw
    );

    auto conv_weights_usr_md = memory::desc(
        {FOut, FIn, K, K},
        memory::data_type::f32,
        memory::format_tag::oihw
    );

    auto conv_bias_usr_md = memory::desc(
        {FOut},
        memory::data_type::f32,
        memory::format_tag::x
    );

    auto conv_weights_usr_mem = memory(conv_weights_usr_md, cpu_engine, conv_weights_buf.data());
    auto conv_bias_usr_mem = memory(conv_bias_usr_md, cpu_engine, conv_bias_buf.data());

    // Create memory objects with a data format selected by the convolution primitive
    auto conv_src_md = memory::desc(
        {BATCH_SIZE, FIn, N + 2, N + 2},
        memory::data_type::f32,
        memory::format_tag::any
    );

    auto conv_weights_md = memory::desc(
        {FOut, FIn, K, K},
        memory::data_type::f32,
        memory::format_tag::any
    );

    auto conv_bias_md = memory::desc(
        {FOut},
        memory::data_type::f32,
        memory::format_tag::any
    );

    auto output_md = memory::desc(
        {BATCH_SIZE, FOut, N, N},
        memory::data_type::f32,
        memory::format_tag::any
    );

    // Create the convolution primitive descriptor, so as to get
    // the data format selected by the primitive.
    auto conv_d = convolution_forward::desc(
        prop_kind::forward_inference,
        algorithm::convolution_direct,
        conv_src_md,
        conv_weights_md,
        conv_bias_md,
        output_md,
        conv_strides,
        conv_padding,
        conv_padding
    );

    auto conv_pd = convolution_forward::primitive_desc(
        conv_d,
        cpu_engine
    );

    auto conv_dst_mem = memory(conv_pd.dst_desc(), cpu_engine);

    // Edit user data format
    auto input_usr_mem = memory(input_usr_md, cpu_engine, input_buf.data());
    auto input_mem = memory(conv_pd.src_desc(), cpu_engine);

    reorder(input_usr_mem, input_mem)
        .execute(cpu_stream, input_usr_mem, input_mem);

    auto conv_weights_mem = conv_weights_usr_mem;
    if (conv_pd.weights_desc() != conv_weights_usr_mem.get_desc()) {
        conv_weights_mem = memory(conv_pd.weights_desc(), cpu_engine);
        reorder(conv_weights_usr_mem, conv_weights_mem)
            .execute(cpu_stream, conv_weights_usr_mem, conv_weights_mem);
    }

    // Add convolution to the network
    net.push_back(convolution_forward(conv_pd));
    net_args.push_back({
        {MKLDNN_ARG_SRC, input_mem},
        {MKLDNN_ARG_WEIGHTS, conv_weights_mem},
        {MKLDNN_ARG_BIAS, conv_bias_usr_mem},
        {MKLDNN_ARG_DST, conv_dst_mem}
    });

    // Execute the network
    for (int i = 0; i < NB_TESTS; ++i) {
        start = rtclock();

        for (size_t j = 0; j < net.size(); ++j)
            net[j].execute(cpu_stream, net_args[j]);

        cpu_stream.wait();

        end = rtclock();
        duration_vector.push_back((end - start) * 1000);
    }

    std::cout << "\n\n\tConv time : " << median(duration_vector) << " ms." << std::endl;

    // Convert convolution output to user data format
    auto output_usr_md = memory::desc(
        {BATCH_SIZE, FOut, N, N},
        memory::data_type::f32,
        memory::format_tag::nchw
    );

    auto output_mem = memory(output_usr_md, cpu_engine);
    reorder(conv_dst_mem, output_mem)
        .execute(cpu_stream, conv_dst_mem, output_mem);

    /* Write results to file */
    float* output = (float*)output_mem.get_data_handle();
    FILE* f = fopen("mkl_result.txt", "w");
    if (f == NULL) {
        std::cout << "Error creating mkl_result.txt" << std::endl;;
        return ;
    }

    for (int n = 0; n < BATCH_SIZE; ++n)
        for (int fout = 0; fout < FOut; ++fout)
            for (int y = 0; y < N; ++y)
                for (int x = 0; x < N; ++x)
                    fprintf(f, "%.10g\n", output[x + y*N + fout*N*N + n*N*N*FOut]);

    fclose(f);
}

int main(int argc, char **argv)
{


{
  folly::Random::DefaultGenerator rng;
  rng.seed(2);
  int32_t count = 0;
  // The Generates test data with consecutive numbers within the same size range
  // to test bulk decode.
  testInts<int64_t, true, true>([&]() -> int64_t {
    auto mod = ++count % 33;
    auto numBytes = mod < 9 ? 1 : mod < 14 ? 2 : mod < 17 ? 3 : mod % 9;
    auto value = folly::Random::rand64(rng) & ((1UL << (7 * numBytes)) - 1);
    return folly::Random::rand32(rng) & 1 ? -value : value;
  });
}

/*
 *
 */

#include <folly/Random.h>
#include "velox/common/base/Nulls.h"
#include "velox/dwio/dwrf/common/IntDecoder.h"
#include "velox/dwio/dwrf/common/IntEncoder.h"
#include "velox/dwio/dwrf/test/OrcTest.h"

#include <gtest/gtest.h>

using namespace facebook::velox::dwio::common;
using namespace facebook::velox;
using namespace facebook::velox::dwrf;

template <typename T, bool isSigned, bool vInt>
void testInts(std::function<T()> generator) {
  auto scopedPool = memory::getDefaultScopedMemoryPool();
  auto& pool = *scopedPool;
  constexpr size_t count = 10240;
  DataBuffer<T> buffer{pool, count};
  std::array<uint64_t, count / 64> nulls;
  for (size_t i = 0; i < count; ++i) {
    buffer[i] = generator();
    bits::setNull(nulls.data(), i, !(i % 2));
  }

  constexpr size_t capacity =
      count * (vInt ? folly::kMaxVarintLength64 : sizeof(T));
  MemorySink sink{pool, capacity};
  DataBufferHolder holder{pool, capacity, 0, DEFAULT_PAGE_GROW_RATIO, &sink};
  auto output = std::make_unique<BufferedOutputStream>(holder);
  auto encoder =
      IntEncoder<isSigned>::createDirect(std::move(output), vInt, sizeof(T));

  encoder->add(buffer.data(), Ranges::of(0, count), nulls.data());

  TestPositionRecorder recorder;
  recorder.addEntry();
  recorder.addEntry();
  encoder->recordPosition(recorder);
  encoder->recordPosition(recorder, 1);

  EXPECT_EQ(recorder.getPositions(0).size(), 0);
  constexpr size_t expectedSize = capacity / 2;
  auto& pos = recorder.getPositions(1);
  EXPECT_EQ(pos.size(), 1);
  if (vInt) {
    EXPECT_GT(pos.at(0), 0);
  } else {
    EXPECT_EQ(pos.at(0), expectedSize);
  }
  auto& latestPos = recorder.getPositions();
  EXPECT_EQ(latestPos.size(), 1);
  if (vInt) {
    EXPECT_GT(latestPos.at(0), 0);
  } else {
    EXPECT_EQ(latestPos.at(0), expectedSize);
  }

  encoder->flush();
  EXPECT_EQ(sink.size(), pos.at(0));
  EXPECT_EQ(sink.size(), latestPos.at(0));

  auto input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, expectedSize);
  auto decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));

  std::array<int64_t, count / 2> vals;
  decoder->next(vals.data(), count / 2, nullptr);
  for (size_t i = 0; i < count / 2; ++i) {
    ASSERT_EQ(buffer[i * 2 + 1], static_cast<T>(vals[i]));
  }

  input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, 100);
  decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));
  int32_t numRead = 0;
  int32_t stride = 1;
  while (numRead < count / 2) {
    if (numRead + stride >= count / 2) {
      break;
    }
    decoder->skipLongsFast(stride);
    numRead += stride;
    stride = (stride * 2 + 1) % 31;
    int64_t num;
    decoder->next(&num, 1, nullptr);
    ASSERT_EQ(buffer[numRead * 2 + 1], static_cast<T>(num));
    ++numRead;
  }

  // Bulk read consecutive.
  input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, 100);
  decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));
  std::vector<uint64_t> result(count / 2);
  decoder->bulkRead(count / 2, result.data());
  for (auto i = 0; i < count / 2; ++i) {
    ASSERT_EQ(buffer[i * 2 + 1], result[i]);
  }
  std::vector<int32_t> rows;
  folly::Random::DefaultGenerator rng;
  rng.seed(1);
  for (auto i = 0; i < count / 2; ++i) {
    rows.push_back(i);
    auto random = folly::Random::rand32(rng) % 20;
    if (random < 13) {
      continue;
    }
    i += random - 10;
  }
  input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, 100);
  decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));
  decoder->bulkReadRows(rows, result.data());
  for (auto i = 0; i < rows.size(); ++i) {
    ASSERT_EQ(buffer[rows[i] * 2 + 1], result[i]);
  }
}

TEST(TestDirect, fixedWidthShort) {
  testInts<int16_t, true, false>([]() -> int16_t {
    return static_cast<int16_t>(folly::Random::rand32());
  });
}

TEST(TestDirect, fixedWidthInt) {
  testInts<int32_t, true, false>(
      []() -> int32_t { return folly::Random::rand32(); });
}

TEST(TestDirect, fixedWidthLong) {
  testInts<int64_t, true, false>(
      []() -> int64_t { return folly::Random::rand64(); });
}

TEST(TestDirect, vIntSignedShort) {
  folly::Random::DefaultGenerator rng;
  rng.seed(2);
  testInts<int16_t, true, true>(
      [&]() -> int16_t { return folly::Random::rand32(rng); });
}

TEST(TestDirect, vIntSignedInt) {
  folly::Random::DefaultGenerator rng;
  rng.seed(2);
  testInts<int64_t, true, true>(
      [&]() -> int32_t { return folly::Random::rand32(rng); });
}

TEST(TestDirect, vIntSignedLong)

TEST(TestDirect, vIntUnsignedLong) {
  folly::Random::DefaultGenerator rng;
  rng.seed(1);
  int32_t count = 0;
  // The Generates test data with consecutive numbers within the same size range
  // to test bulk decode.
  testInts<int64_t, false, true>([&]() -> int64_t {
    auto mod = ++count % 33;
    auto numBytes = mod < 9 ? 1 : mod < 14 ? 2 : mod < 17 ? 3 : mod % 9;
    return folly::Random::rand64(rng) & ((1UL << (7 * numBytes)) - 1);
  });
}

template <bool isSigned>
void testCorruptedVarInts() {
  std::vector<uint8_t> invalidInt{
      0X01, // 1st number - 1
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0x81 // Invalid number.
  };
  auto input = std::make_unique<SeekableArrayInputStream>(
      invalidInt.data(), invalidInt.size());
  auto decoder = IntDecoder<isSigned>::createDirect(
      std::move(input), true, sizeof(int64_t));

  std::array<int64_t, 2> vals;
  // First value is always read on the slow path.
  decoder->next(vals.data(), 1, nullptr);
  EXPECT_THROW(decoder->next(vals.data(), 1, nullptr);
               , exception::LoggedException);
}

TEST(TestDirect, corruptedInts) {
  testCorruptedVarInts<false>();
  testCorruptedVarInts<true>();
}


while (numRead < count / 2) {
    if (numRead + stride >= count / 2) {
      break;
    }
    decoder->skipLongsFast(stride);
    numRead += stride;
    stride = (stride * 2 + 1) % 31;
    int64_t num;
    decoder->next(&num, 1, nullptr);
    ASSERT_EQ(buffer[numRead * 2 + 1], static_cast<T>(num));
    ++numRead;
  }

/*
 *
 */

#include <folly/Random.h>
#include "velox/common/base/Nulls.h"
#include "velox/dwio/dwrf/common/IntDecoder.h"
#include "velox/dwio/dwrf/common/IntEncoder.h"
#include "velox/dwio/dwrf/test/OrcTest.h"

#include <gtest/gtest.h>

using namespace facebook::velox::dwio::common;
using namespace facebook::velox;
using namespace facebook::velox::dwrf;

template <typename T, bool isSigned, bool vInt>
void testInts(std::function<T()> generator) {
  auto scopedPool = memory::getDefaultScopedMemoryPool();
  auto& pool = *scopedPool;
  constexpr size_t count = 10240;
  DataBuffer<T> buffer{pool, count};
  std::array<uint64_t, count / 64> nulls;
  for (size_t i = 0; i < count; ++i) {
    buffer[i] = generator();
    bits::setNull(nulls.data(), i, !(i % 2));
  }

  constexpr size_t capacity =
      count * (vInt ? folly::kMaxVarintLength64 : sizeof(T));
  MemorySink sink{pool, capacity};
  DataBufferHolder holder{pool, capacity, 0, DEFAULT_PAGE_GROW_RATIO, &sink};
  auto output = std::make_unique<BufferedOutputStream>(holder);
  auto encoder =
      IntEncoder<isSigned>::createDirect(std::move(output), vInt, sizeof(T));

  encoder->add(buffer.data(), Ranges::of(0, count), nulls.data());

  TestPositionRecorder recorder;
  recorder.addEntry();
  recorder.addEntry();
  encoder->recordPosition(recorder);
  encoder->recordPosition(recorder, 1);

  EXPECT_EQ(recorder.getPositions(0).size(), 0);
  constexpr size_t expectedSize = capacity / 2;
  auto& pos = recorder.getPositions(1);
  EXPECT_EQ(pos.size(), 1);
  if (vInt) {
    EXPECT_GT(pos.at(0), 0);
  } else {
    EXPECT_EQ(pos.at(0), expectedSize);
  }
  auto& latestPos = recorder.getPositions();
  EXPECT_EQ(latestPos.size(), 1);
  if (vInt) {
    EXPECT_GT(latestPos.at(0), 0);
  } else {
    EXPECT_EQ(latestPos.at(0), expectedSize);
  }

  encoder->flush();
  EXPECT_EQ(sink.size(), pos.at(0));
  EXPECT_EQ(sink.size(), latestPos.at(0));

  auto input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, expectedSize);
  auto decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));

  std::array<int64_t, count / 2> vals;
  decoder->next(vals.data(), count / 2, nullptr);
  for (size_t i = 0; i < count / 2; ++i) {
    ASSERT_EQ(buffer[i * 2 + 1], static_cast<T>(vals[i]));
  }

  input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, 100);
  decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));
  int32_t numRead = 0;
  int32_t stride = 1;

  // Bulk read consecutive.
  input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, 100);
  decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));
  std::vector<uint64_t> result(count / 2);
  decoder->bulkRead(count / 2, result.data());
  for (auto i = 0; i < count / 2; ++i) {
    ASSERT_EQ(buffer[i * 2 + 1], result[i]);
  }
  std::vector<int32_t> rows;
  folly::Random::DefaultGenerator rng;
  rng.seed(1);
  for (auto i = 0; i < count / 2; ++i) {
    rows.push_back(i);
    auto random = folly::Random::rand32(rng) % 20;
    if (random < 13) {
      continue;
    }
    i += random - 10;
  }
  input = std::make_unique<SeekableArrayInputStream>(
      sink.getData(), expectedSize, 100);
  decoder =
      IntDecoder<isSigned>::createDirect(std::move(input), vInt, sizeof(T));
  decoder->bulkReadRows(rows, result.data());
  for (auto i = 0; i < rows.size(); ++i) {
    ASSERT_EQ(buffer[rows[i] * 2 + 1], result[i]);
  }
}

TEST(TestDirect, fixedWidthShort) {
  testInts<int16_t, true, false>([]() -> int16_t {
    return static_cast<int16_t>(folly::Random::rand32());
  });
}

TEST(TestDirect, fixedWidthInt) {
  testInts<int32_t, true, false>(
      []() -> int32_t { return folly::Random::rand32(); });
}

TEST(TestDirect, fixedWidthLong) {
  testInts<int64_t, true, false>(
      []() -> int64_t { return folly::Random::rand64(); });
}

TEST(TestDirect, vIntSignedShort) {
  folly::Random::DefaultGenerator rng;
  rng.seed(2);
  testInts<int16_t, true, true>(
      [&]() -> int16_t { return folly::Random::rand32(rng); });
}

TEST(TestDirect, vIntSignedInt) {
  folly::Random::DefaultGenerator rng;
  rng.seed(2);
  testInts<int64_t, true, true>(
      [&]() -> int32_t { return folly::Random::rand32(rng); });
}

TEST(TestDirect, vIntSignedLong) {
  folly::Random::DefaultGenerator rng;
  rng.seed(2);
  int32_t count = 0;
  // The Generates test data with consecutive numbers within the same size range
  // to test bulk decode.
  testInts<int64_t, true, true>([&]() -> int64_t {
    auto mod = ++count % 33;
    auto numBytes = mod < 9 ? 1 : mod < 14 ? 2 : mod < 17 ? 3 : mod % 9;
    auto value = folly::Random::rand64(rng) & ((1UL << (7 * numBytes)) - 1);
    return folly::Random::rand32(rng) & 1 ? -value : value;
  });
}

TEST(TestDirect, vIntUnsignedLong) {
  folly::Random::DefaultGenerator rng;
  rng.seed(1);
  int32_t count = 0;
  // The Generates test data with consecutive numbers within the same size range
  // to test bulk decode.
  testInts<int64_t, false, true>([&]() -> int64_t {
    auto mod = ++count % 33;
    auto numBytes = mod < 9 ? 1 : mod < 14 ? 2 : mod < 17 ? 3 : mod % 9;
    return folly::Random::rand64(rng) & ((1UL << (7 * numBytes)) - 1);
  });
}

template <bool isSigned>
void testCorruptedVarInts() {
  std::vector<uint8_t> invalidInt{
      0X01, // 1st number - 1
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0xFF,
      0x81 // Invalid number.
  };
  auto input = std::make_unique<SeekableArrayInputStream>(
      invalidInt.data(), invalidInt.size());
  auto decoder = IntDecoder<isSigned>::createDirect(
      std::move(input), true, sizeof(int64_t));

  std::array<int64_t, 2> vals;
  // First value is always read on the slow path.
  decoder->next(vals.data(), 1, nullptr);
  EXPECT_THROW(decoder->next(vals.data(), 1, nullptr);
               , exception::LoggedException);
}

TEST(TestDirect, corruptedInts) {
  testCorruptedVarInts<false>();
  testCorruptedVarInts<true>();
}


{
#ifdef __linux__
	pthread_setname_np(pthread_self(), name);
#else
	// TODO: Kinda messy.
	(void)name;
#endif
}

#include "thread_name.hpp"

#ifdef __linux__
#include <pthread.h>
#endif

namespace Util
{
void set_current_thread_name(const char *name)


{
	CScriptAny *self = (CScriptAny*)gen->GetObject();
	self->AddRef();
}

#include "scriptany.h"
#include <new>
#include <assert.h>
#include <string.h>

BEGIN_AS_NAMESPACE

// We'll use the generic interface for the factories as we need the engine pointer
static void ScriptAnyFactory_Generic(asIScriptGeneric *gen)
{
	asIScriptEngine *engine = gen->GetEngine();

	*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(engine);
}

static void ScriptAnyFactory2_Generic(asIScriptGeneric *gen)
{
	asIScriptEngine *engine = gen->GetEngine();
	void *ref = (void*)gen->GetArgAddress(0);
	int refType = gen->GetArgTypeId(0);

	*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(ref,refType,engine);
}

static CScriptAny &ScriptAnyAssignment(CScriptAny *other, CScriptAny *self)
{
	return *self = *other;
}

static void ScriptAnyAssignment_Generic(asIScriptGeneric *gen)
{
	CScriptAny *other = (CScriptAny*)gen->GetArgObject(0);
	CScriptAny *self = (CScriptAny*)gen->GetObject();

	*self = *other;

	gen->SetReturnObject(self);
}

static void ScriptAny_Store_Generic(asIScriptGeneric *gen)
{
	void *ref = (void*)gen->GetArgAddress(0);
	int refTypeId = gen->GetArgTypeId(0);
	CScriptAny *self = (CScriptAny*)gen->GetObject();

	self->Store(ref, refTypeId);
}

static void ScriptAny_StoreInt_Generic(asIScriptGeneric *gen)
{
	asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
	CScriptAny *self = (CScriptAny*)gen->GetObject();

	self->Store(*ref);
}

static void ScriptAny_StoreFlt_Generic(asIScriptGeneric *gen)
{
	double *ref = (double*)gen->GetArgAddress(0);
	CScriptAny *self = (CScriptAny*)gen->GetObject();

	self->Store(*ref);
}

static void ScriptAny_Retrieve_Generic(asIScriptGeneric *gen)
{
	void *ref = (void*)gen->GetArgAddress(0);
	int refTypeId = gen->GetArgTypeId(0);
	CScriptAny *self = (CScriptAny*)gen->GetObject();

	*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(ref, refTypeId);
}

static void ScriptAny_RetrieveInt_Generic(asIScriptGeneric *gen)
{
	asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
	CScriptAny *self = (CScriptAny*)gen->GetObject();

	*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
}

static void ScriptAny_RetrieveFlt_Generic(asIScriptGeneric *gen)
{
	double *ref = (double*)gen->GetArgAddress(0);
	CScriptAny *self = (CScriptAny*)gen->GetObject();

	*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
}

static void ScriptAny_AddRef_Generic(asIScriptGeneric *gen)

static void ScriptAny_Release_Generic(asIScriptGeneric *gen)
{
	CScriptAny *self = (CScriptAny*)gen->GetObject();
	self->Release();
}

static void ScriptAny_GetRefCount_Generic(asIScriptGeneric *gen)
{
	CScriptAny *self = (CScriptAny*)gen->GetObject();
	*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}

static void ScriptAny_SetFlag_Generic(asIScriptGeneric *gen)
{
	CScriptAny *self = (CScriptAny*)gen->GetObject();
	self->SetFlag();
}

static void ScriptAny_GetFlag_Generic(asIScriptGeneric *gen)
{
	CScriptAny *self = (CScriptAny*)gen->GetObject();
	*(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag();
}

static void ScriptAny_EnumReferences_Generic(asIScriptGeneric *gen)
{
	CScriptAny *self = (CScriptAny*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->EnumReferences(engine);
}

static void ScriptAny_ReleaseAllHandles_Generic(asIScriptGeneric *gen)
{
	CScriptAny *self = (CScriptAny*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->ReleaseAllHandles(engine);
}

void RegisterScriptAny(asIScriptEngine *engine)
{
	if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
		RegisterScriptAny_Generic(engine);
	else
		RegisterScriptAny_Native(engine);
}

void RegisterScriptAny_Native(asIScriptEngine *engine)
{
	int r;
	r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 );

	// We'll use the generic interface for the constructor as we need the engine pointer
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);

	r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptAny,AddRef), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptAny,Release), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "void store(?&in)", asMETHODPR(CScriptAny,Store,(void*,int),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asMETHODPR(CScriptAny,Store,(asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "void store(const double&in)", asMETHODPR(CScriptAny,Store,(double&),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "bool retrieve(?&out)", asMETHODPR(CScriptAny,Retrieve,(void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out)", asMETHODPR(CScriptAny,Retrieve,(asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "bool retrieve(double&out)", asMETHODPR(CScriptAny,Retrieve,(double&) const,bool), asCALL_THISCALL); assert( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptAny,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptAny,SetFlag), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptAny,GetFlag), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptAny,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptAny,ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
}

void RegisterScriptAny_Generic(asIScriptEngine *engine)
{
	int r;
	r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 );

	// We'll use the generic interface for the constructor as we need the engine pointer
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);

	r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptAny_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptAny_Release_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "void store(?&in)", asFUNCTION(ScriptAny_Store_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asFUNCTION(ScriptAny_StoreInt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "void store(const double&in)", asFUNCTION(ScriptAny_StoreFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "bool retrieve(?&out) const", asFUNCTION(ScriptAny_Retrieve_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out) const", asFUNCTION(ScriptAny_RetrieveInt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("any", "bool retrieve(double&out) const", asFUNCTION(ScriptAny_RetrieveFlt_Generic), asCALL_GENERIC); assert( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptAny_GetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptAny_SetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptAny_GetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptAny_EnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptAny_ReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 );
}

CScriptAny &CScriptAny::operator=(const CScriptAny &other)
{
	// Hold on to the object type reference so it isn't destroyed too early
	if( (other.value.typeId & asTYPEID_MASK_OBJECT) )
	{
		asITypeInfo *ti = engine->GetTypeInfoById(other.value.typeId);
		if( ti )
			ti->AddRef();
	}

	FreeObject();

	value.typeId = other.value.typeId;
	if( value.typeId & asTYPEID_OBJHANDLE )
	{
		// For handles, copy the pointer and increment the reference count
		value.valueObj = other.value.valueObj;
		engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
	}
	else if( value.typeId & asTYPEID_MASK_OBJECT )
	{
		// Create a copy of the object
		value.valueObj = engine->CreateScriptObjectCopy(other.value.valueObj, engine->GetTypeInfoById(value.typeId));
	}
	else
	{
		// Primitives can be copied directly
		value.valueInt = other.value.valueInt;
	}

	return *this;
}

int CScriptAny::CopyFrom(const CScriptAny *other)
{
	if( other == 0 ) return asINVALID_ARG;

	*this = *(CScriptAny*)other;

	return 0;
}

CScriptAny::CScriptAny(asIScriptEngine *engine)
{
	this->engine = engine;
	refCount = 1;
	gcFlag = false;

	value.typeId = 0;
	value.valueInt = 0;

	// Notify the garbage collector of this object
	engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));
}

CScriptAny::CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine)
{
	this->engine = engine;
	refCount = 1;
	gcFlag = false;

	value.typeId = 0;
	value.valueInt = 0;

	// Notify the garbage collector of this object
	engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));

	Store(ref, refTypeId);
}

CScriptAny::~CScriptAny()
{
	FreeObject();
}

void CScriptAny::Store(void *ref, int refTypeId)
{
	// This method is not expected to be used for primitive types, except for bool, int64, or double
	assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_VOID || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );

	// Hold on to the object type reference so it isn't destroyed too early
	if( (refTypeId & asTYPEID_MASK_OBJECT) )
	{
		asITypeInfo *ti = engine->GetTypeInfoById(refTypeId);
		if( ti )
			ti->AddRef();
	}

	FreeObject();

	value.typeId = refTypeId;
	if( value.typeId & asTYPEID_OBJHANDLE )
	{
		// We're receiving a reference to the handle, so we need to dereference it
		value.valueObj = *(void**)ref;
		engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
	}
	else if( value.typeId & asTYPEID_MASK_OBJECT )
	{
		// Create a copy of the object
		value.valueObj = engine->CreateScriptObjectCopy(ref, engine->GetTypeInfoById(value.typeId));
	}
	else
	{
		// Primitives can be copied directly
		value.valueInt = 0;

		// Copy the primitive value
		// We receive a pointer to the value.
		int size = engine->GetSizeOfPrimitiveType(value.typeId);
		memcpy(&value.valueInt, ref, size);
	}
}

void CScriptAny::Store(double &ref)
{
	Store(&ref, asTYPEID_DOUBLE);
}

void CScriptAny::Store(asINT64 &ref)
{
	Store(&ref, asTYPEID_INT64);
}

bool CScriptAny::Retrieve(void *ref, int refTypeId) const
{
	// This method is not expected to be used for primitive types, except for bool, int64, or double
	assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );

	if( refTypeId & asTYPEID_OBJHANDLE )
	{
		// Is the handle type compatible with the stored value?

		// A handle can be retrieved if the stored type is a handle of same or compatible type
		// or if the stored type is an object that implements the interface that the handle refer to.
		if( (value.typeId & asTYPEID_MASK_OBJECT) )
		{
			// Don't allow the retrieval if the stored handle is to a const object but not the wanted handle
			if( (value.typeId & asTYPEID_HANDLETOCONST) && !(refTypeId & asTYPEID_HANDLETOCONST) )
				return false;

			// RefCastObject will increment the refCount of the returned pointer if successful
			engine->RefCastObject(value.valueObj, engine->GetTypeInfoById(value.typeId), engine->GetTypeInfoById(refTypeId), reinterpret_cast<void**>(ref));
			if( *(asPWORD*)ref == 0 )
				return false;
			return true;
		}
	}
	else if( refTypeId & asTYPEID_MASK_OBJECT )
	{
		// Is the object type compatible with the stored value?

		// Copy the object into the given reference
		if( value.typeId == refTypeId )
		{
			engine->AssignScriptObject(ref, value.valueObj, engine->GetTypeInfoById(value.typeId));
			return true;
		}
	}
	else
	{
		// Is the primitive type compatible with the stored value?

		if( value.typeId == refTypeId )
		{
			int size = engine->GetSizeOfPrimitiveType(refTypeId);
			memcpy(ref, &value.valueInt, size);
			return true;
		}

		// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
		if( value.typeId == asTYPEID_INT64 && refTypeId == asTYPEID_DOUBLE )
		{
			*(double*)ref = double(value.valueInt);
			return true;
		}
		else if( value.typeId == asTYPEID_DOUBLE && refTypeId == asTYPEID_INT64 )
		{
			*(asINT64*)ref = asINT64(value.valueFlt);
			return true;
		}
	}

	return false;
}

bool CScriptAny::Retrieve(asINT64 &outValue) const
{
	return Retrieve(&outValue, asTYPEID_INT64);
}

bool CScriptAny::Retrieve(double &outValue) const
{
	return Retrieve(&outValue, asTYPEID_DOUBLE);
}

int CScriptAny::GetTypeId() const
{
	return value.typeId;
}

void CScriptAny::FreeObject()
{
	// If it is a handle or a ref counted object, call release
	if( value.typeId & asTYPEID_MASK_OBJECT )
	{
		// Let the engine release the object
		asITypeInfo *ti = engine->GetTypeInfoById(value.typeId);
		engine->ReleaseScriptObject(value.valueObj, ti);

		// Release the object type info
		if( ti )
			ti->Release();

		value.valueObj = 0;
		value.typeId = 0;
	}

	// For primitives, there's nothing to do
}

void CScriptAny::EnumReferences(asIScriptEngine *inEngine)
{
	// If we're holding a reference, we'll notify the garbage collector of it
	if( value.valueObj && (value.typeId & asTYPEID_MASK_OBJECT) )
	{
		inEngine->GCEnumCallback(value.valueObj);

		// The object type itself is also garbage collected
		asITypeInfo *ti = inEngine->GetTypeInfoById(value.typeId);
		if( ti )
			inEngine->GCEnumCallback(ti);
	}
}

void CScriptAny::ReleaseAllHandles(asIScriptEngine * /*engine*/)
{
	FreeObject();
}

int CScriptAny::AddRef() const
{
	// Increase counter and clear flag set by GC
	gcFlag = false;
	return refCount.fetch_add(1) + 1;
}

int CScriptAny::Release() const
{
	// Decrease the ref counter
	gcFlag = false;
	if( refCount.fetch_sub(1) == 1 )
	{
		// Delete this object as no more references to it exists
		delete this;
		return 0;
	}

	return refCount;
}

int CScriptAny::GetRefCount()
{
	return refCount;
}

void CScriptAny::SetFlag()
{
	gcFlag = true;
}

bool CScriptAny::GetFlag()
{
	return gcFlag;
}

END_AS_NAMESPACE


{
    if (format.empty())
        return true;
    try {
        std::regex regex(format);
        return regex_match(input, regex);
    } catch (const std::regex_error &) {
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9)
        std::cerr << "Warning: cannot validate text field due to lacking regular expression support. please compile with GCC >= 4.9" << std::endl;
        return true;
#else
        throw;
#endif
    }
}

/*
    src/textbox.cpp -- Fancy text box with builtin regular
    expression-based validation

    The text box widget was contributed by Christian Schueller.

    NanoGUI was developed by Wenzel Jakob <example@nnthink.com>.
    The widget drawing code is based on the NanoVG demo application
    by Mikko Mononen.

*/

#include <nanogui/window.h>
#include <nanogui/screen.h>
#include <nanogui/textbox.h>
#include <nanogui/opengl.h>
#include <nanogui/theme.h>
#include <regex>
#include <iostream>

NAMESPACE_BEGIN(nanogui)

TextBox::TextBox(Widget *parent, const std::string &value)
    : Widget(parent),
      m_editable(false),
      m_spinnable(false),
      m_committed(true),
      m_value(value),
      m_default_value(""),
      m_alignment(Alignment::Center),
      m_units(""),
      m_format(""),
      m_units_image(-1),
      m_valid_format(true),
      m_value_temp(value),
      m_cursor_pos(-1),
      m_selection_pos(-1),
      m_mouse_pos(Vector2i(-1,-1)),
      m_mouse_down_pos(Vector2i(-1,-1)),
      m_mouse_drag_pos(Vector2i(-1,-1)),
      m_mouse_down_modifier(0),
      m_text_offset(0),
      m_last_click(0) {
    if (m_theme) m_font_size = m_theme->m_text_box_font_size;
    m_icon_extra_scale = .8f;
}

void TextBox::set_editable(bool editable) {
    m_editable = editable;
    set_cursor(editable ? Cursor::IBeam : Cursor::Arrow);
}

void TextBox::set_theme(Theme *theme) {
    Widget::set_theme(theme);
    if (m_theme)
        m_font_size = m_theme->m_text_box_font_size;
}

Vector2i TextBox::preferred_size(NVGcontext *ctx) const {
    Vector2i size(0, font_size() * 1.4f);

    float uw = 0;
    if (m_units_image > 0) {
        int w, h;
        nvgImageSize(ctx, m_units_image, &w, &h);
        float uh = size[1] * 0.4f;
        uw = w * uh / h;
    } else if (!m_units.empty()) {
        uw = nvgTextBounds(ctx, 0, 0, m_units.c_str(), nullptr, nullptr);
    }
    float sw = 0;
    if (m_spinnable) {
        sw = 14.f;
    }

    float ts = nvgTextBounds(ctx, 0, 0, m_value.c_str(), nullptr, nullptr);
    size[0] = size[1] + ts + uw + sw;
    return size;
}

void TextBox::draw(NVGcontext* ctx) {
    Widget::draw(ctx);

    NVGpaint bg = nvgBoxGradient(ctx,
        m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2, m_size.y() - 2,
        3, 4, Color(255, 32), Color(32, 32));
    NVGpaint fg1 = nvgBoxGradient(ctx,
        m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2, m_size.y() - 2,
        3, 4, Color(150, 32), Color(32, 32));
    NVGpaint fg2 = nvgBoxGradient(ctx,
        m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2, m_size.y() - 2,
        3, 4, nvgRGBA(255, 0, 0, 100), nvgRGBA(255, 0, 0, 50));

    nvgBeginPath(ctx);
    nvgRoundedRect(ctx, m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2,
                   m_size.y() - 2, 3);

    if (m_editable && focused())
        m_valid_format ? nvgFillPaint(ctx, fg1) : nvgFillPaint(ctx, fg2);
    else if (m_spinnable && m_mouse_down_pos.x() != -1)
        nvgFillPaint(ctx, fg1);
    else
        nvgFillPaint(ctx, bg);

    nvgFill(ctx);

    nvgBeginPath(ctx);
    nvgRoundedRect(ctx, m_pos.x() + 0.5f, m_pos.y() + 0.5f, m_size.x() - 1,
                   m_size.y() - 1, 2.5f);
    nvgStrokeColor(ctx, Color(0, 48));
    nvgStroke(ctx);

    nvgFontSize(ctx, font_size());
    nvgFontFace(ctx, "sans");
    Vector2i draw_pos(m_pos.x(), m_pos.y() + m_size.y() * 0.5f + 1);

    float x_spacing = m_size.y() * 0.3f;

    float unit_width = 0;

    if (m_units_image > 0) {
        int w, h;
        nvgImageSize(ctx, m_units_image, &w, &h);
        float unit_height = m_size.y() * 0.4f;
        unit_width = w * unit_height / h;
        NVGpaint img_paint = nvgImagePattern(
            ctx, m_pos.x() + m_size.x() - x_spacing - unit_width,
            draw_pos.y() - unit_height * 0.5f, unit_width, unit_height, 0,
            m_units_image, m_enabled ? 0.7f : 0.35f);
        nvgBeginPath(ctx);
        nvgRect(ctx, m_pos.x() + m_size.x() - x_spacing - unit_width,
                draw_pos.y() - unit_height * 0.5f, unit_width, unit_height);
        nvgFillPaint(ctx, img_paint);
        nvgFill(ctx);
        unit_width += 2;
    } else if (!m_units.empty()) {
        unit_width = nvgTextBounds(ctx, 0, 0, m_units.c_str(), nullptr, nullptr);
        nvgFillColor(ctx, Color(255, m_enabled ? 64 : 32));
        nvgTextAlign(ctx, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE);
        nvgText(ctx, m_pos.x() + m_size.x() - x_spacing, draw_pos.y(),
                m_units.c_str(), nullptr);
        unit_width += 2;
    }

    float spin_arrows_width = 0.f;

    if (m_spinnable && !focused()) {
        spin_arrows_width = 14.f;

        nvgFontFace(ctx, "icons");
        nvgFontSize(ctx, ((m_font_size < 0) ? m_theme->m_button_font_size : m_font_size) * icon_scale());

        bool spinning = m_mouse_down_pos.x() != -1;

        /* up button */ {
            bool hover = m_mouse_focus && spin_area(m_mouse_pos) == SpinArea::Top;
            nvgFillColor(ctx, (m_enabled && (hover || spinning)) ? m_theme->m_text_color : m_theme->m_disabled_text_color);
            auto icon = utf8(m_theme->m_text_box_up_icon);
            nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
            Vector2f icon_pos(m_pos.x() + 4.f,
                             m_pos.y() + m_size.y()/2.f - x_spacing/2.f);
            nvgText(ctx, icon_pos.x(), icon_pos.y(), icon.data(), nullptr);
        }

        /* down button */ {
            bool hover = m_mouse_focus && spin_area(m_mouse_pos) == SpinArea::Bottom;
            nvgFillColor(ctx, (m_enabled && (hover || spinning)) ? m_theme->m_text_color : m_theme->m_disabled_text_color);
            auto icon = utf8(m_theme->m_text_box_down_icon);
            nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
            Vector2f icon_pos(m_pos.x() + 4.f,
                             m_pos.y() + m_size.y()/2.f + x_spacing/2.f + 1.5f);
            nvgText(ctx, icon_pos.x(), icon_pos.y(), icon.data(), nullptr);
        }

        nvgFontSize(ctx, font_size());
        nvgFontFace(ctx, "sans");
    }

    switch (m_alignment) {
        case Alignment::Left:
            nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
            draw_pos.x() += x_spacing + spin_arrows_width;
            break;
        case Alignment::Right:
            nvgTextAlign(ctx, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE);
            draw_pos.x() += m_size.x() - unit_width - x_spacing;
            break;
        case Alignment::Center:
            nvgTextAlign(ctx, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
            draw_pos.x() += m_size.x() * 0.5f;
            break;
    }

    nvgFontSize(ctx, font_size());
    nvgFillColor(ctx, m_enabled && (!m_committed || !m_value.empty()) ?
        m_theme->m_text_color :
        m_theme->m_disabled_text_color);

    // clip visible text area
    float clip_x = m_pos.x() + x_spacing + spin_arrows_width - 1.0f;
    float clip_y = m_pos.y() + 1.0f;
    float clip_width = m_size.x() - unit_width - spin_arrows_width - 2 * x_spacing + 2.0f;
    float clip_height = m_size.y() - 3.0f;

    nvgSave(ctx);
    nvgIntersectScissor(ctx, clip_x, clip_y, clip_width, clip_height);

    Vector2i old_draw_pos(draw_pos);
    draw_pos.x() += m_text_offset;

    if (m_committed) {
        nvgText(ctx, draw_pos.x(), draw_pos.y(), m_value.empty() ? m_placeholder.c_str() : m_value.c_str(), nullptr);
    } else {
        const int max_glyphs = 1024;
        NVGglyphPosition glyphs[max_glyphs];
        float text_bound[4];
        nvgTextBounds(ctx, draw_pos.x(), draw_pos.y(), m_value_temp.c_str(),
                      nullptr, text_bound);
        float lineh = text_bound[3] - text_bound[1];

        // find cursor positions
        int nglyphs =
            nvgTextGlyphPositions(ctx, draw_pos.x(), draw_pos.y(),
                                  m_value_temp.c_str(), nullptr, glyphs, max_glyphs);
        update_cursor(ctx, text_bound[2], glyphs, nglyphs);

        // compute text offset
        int prev_cpos = m_cursor_pos > 0 ? m_cursor_pos - 1 : 0;
        int next_cpos = m_cursor_pos < nglyphs ? m_cursor_pos + 1 : nglyphs;
        float prev_cx = cursor_index_to_position(prev_cpos, text_bound[2], glyphs, nglyphs);
        float next_cx = cursor_index_to_position(next_cpos, text_bound[2], glyphs, nglyphs);

        if (next_cx > clip_x + clip_width)
            m_text_offset -= next_cx - (clip_x + clip_width) + 1;
        if (prev_cx < clip_x)
            m_text_offset += clip_x - prev_cx + 1;

        draw_pos.x() = old_draw_pos.x() + m_text_offset;

        // draw text with offset
        nvgText(ctx, draw_pos.x(), draw_pos.y(), m_value_temp.c_str(), nullptr);
        nvgTextBounds(ctx, draw_pos.x(), draw_pos.y(), m_value_temp.c_str(),
                      nullptr, text_bound);

        // recompute cursor positions
        nglyphs = nvgTextGlyphPositions(ctx, draw_pos.x(), draw_pos.y(),
                m_value_temp.c_str(), nullptr, glyphs, max_glyphs);

        if (m_cursor_pos > -1) {
            if (m_selection_pos > -1) {
                float caretx = cursor_index_to_position(m_cursor_pos, text_bound[2],
                                                    glyphs, nglyphs);
                float selx = cursor_index_to_position(m_selection_pos, text_bound[2],
                                                  glyphs, nglyphs);

                if (caretx > selx)
                    std::swap(caretx, selx);

                // draw selection
                nvgBeginPath(ctx);
                nvgFillColor(ctx, nvgRGBA(255, 255, 255, 80));
                nvgRect(ctx, caretx, draw_pos.y() - lineh * 0.5f, selx - caretx,
                        lineh);
                nvgFill(ctx);
            }

            float caretx = cursor_index_to_position(m_cursor_pos, text_bound[2], glyphs, nglyphs);

            // draw cursor
            nvgBeginPath(ctx);
            nvgMoveTo(ctx, caretx, draw_pos.y() - lineh * 0.5f);
            nvgLineTo(ctx, caretx, draw_pos.y() + lineh * 0.5f);
            nvgStrokeColor(ctx, nvgRGBA(255, 192, 0, 255));
            nvgStrokeWidth(ctx, 1.0f);
            nvgStroke(ctx);
        }
    }
    nvgRestore(ctx);
}

bool TextBox::mouse_enter_event(const Vector2i &p, bool enter) {
    Widget::mouse_enter_event(p, enter);
    return true;
}

bool TextBox::mouse_button_event(const Vector2i &p, int button, bool down,
                                 int modifiers) {

    if (button == GLFW_MOUSE_BUTTON_1 && down && !m_focused) {
        if (!m_spinnable || spin_area(p) == SpinArea::None) /* not on scrolling arrows */
            request_focus();
    }

    if (m_editable && focused()) {
        if (down) {
            m_mouse_down_pos = p;
            m_mouse_down_modifier = modifiers;

            double time = glfwGetTime();
            if (time - m_last_click < 0.25) {
                /* Double-click: select all text */
                m_selection_pos = 0;
                m_cursor_pos = (int) m_value_temp.size();
                m_mouse_down_pos = Vector2i(-1, -1);
            }
            m_last_click = time;
        } else {
            m_mouse_down_pos = Vector2i(-1, -1);
            m_mouse_drag_pos = Vector2i(-1, -1);
        }
        return true;
    } else if (m_spinnable && !focused()) {
        if (down) {
            if (spin_area(p) == SpinArea::None) {
                m_mouse_down_pos = p;
                m_mouse_down_modifier = modifiers;

                double time = glfwGetTime();
                if (time - m_last_click < 0.25) {
                    /* Double-click: reset to default value */
                    m_value = m_default_value;
                    if (m_callback)
                        m_callback(m_value);

                    m_mouse_down_pos = Vector2i(-1, -1);
                }
                m_last_click = time;
            } else {
                m_mouse_down_pos = Vector2i(-1, -1);
                m_mouse_drag_pos = Vector2i(-1, -1);
            }
        } else {
            m_mouse_down_pos = Vector2i(-1, -1);
            m_mouse_drag_pos = Vector2i(-1, -1);
        }
        return true;
    }

    return false;
}

bool TextBox::mouse_motion_event(const Vector2i &p, const Vector2i & /* rel */,
                                 int /* button */, int /* modifiers */) {
    m_mouse_pos = p;

    if (!m_editable)
        set_cursor(Cursor::Arrow);
    else if (m_spinnable && !focused() && spin_area(m_mouse_pos) != SpinArea::None) /* scrolling arrows */
        set_cursor(Cursor::Hand);
    else
        set_cursor(Cursor::IBeam);

    return m_editable;
}

bool TextBox::mouse_drag_event(const Vector2i &p, const Vector2i &/* rel */,
                               int /* button */, int /* modifiers */) {
    m_mouse_pos = p;
    m_mouse_drag_pos = p;

    if (m_editable && focused())
        return true;
    return false;
}

bool TextBox::focus_event(bool focused) {
    Widget::focus_event(focused);

    std::string backup = m_value;

    if (m_editable) {
        if (focused) {
            m_value_temp = m_value;
            m_committed = false;
            m_cursor_pos = 0;
        } else {
            if (m_valid_format) {
                if (m_value_temp == "")
                    m_value = m_default_value;
                else
                    m_value = m_value_temp;
            }

            if (m_callback && !m_callback(m_value))
                m_value = backup;

            m_valid_format = true;
            m_committed = true;
            m_cursor_pos = -1;
            m_selection_pos = -1;
            m_text_offset = 0;
        }

        m_valid_format = (m_value_temp == "") || check_format(m_value_temp, m_format);
    }

    return true;
}

bool TextBox::keyboard_event(int key, int /* scancode */, int action, int modifiers) {
    if (m_editable && focused()) {
        if (action == GLFW_PRESS || action == GLFW_REPEAT) {
            if (key == GLFW_KEY_LEFT) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                if (m_cursor_pos > 0)
                    m_cursor_pos--;
            } else if (key == GLFW_KEY_RIGHT) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                if (m_cursor_pos < (int) m_value_temp.length())
                    m_cursor_pos++;
            } else if (key == GLFW_KEY_HOME) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                m_cursor_pos = 0;
            } else if (key == GLFW_KEY_END) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                m_cursor_pos = (int) m_value_temp.size();
            } else if (key == GLFW_KEY_BACKSPACE) {
                if (!delete_selection()) {
                    if (m_cursor_pos > 0) {
                        m_value_temp.erase(m_value_temp.begin() + m_cursor_pos - 1);
                        m_cursor_pos--;
                    }
                }
            } else if (key == GLFW_KEY_DELETE) {
                if (!delete_selection()) {
                    if (m_cursor_pos < (int) m_value_temp.length())
                        m_value_temp.erase(m_value_temp.begin() + m_cursor_pos);
                }
            } else if (key == GLFW_KEY_ENTER) {
                if (!m_committed)
                    focus_event(false);
            } else if (key == GLFW_KEY_A && modifiers == SYSTEM_COMMAND_MOD) {
                m_cursor_pos = (int) m_value_temp.length();
                m_selection_pos = 0;
            } else if (key == GLFW_KEY_X && modifiers == SYSTEM_COMMAND_MOD) {
                copy_selection();
                delete_selection();
            } else if (key == GLFW_KEY_C && modifiers == SYSTEM_COMMAND_MOD) {
                copy_selection();
            } else if (key == GLFW_KEY_V && modifiers == SYSTEM_COMMAND_MOD) {
                delete_selection();
                paste_from_clipboard();
            }

            m_valid_format =
                (m_value_temp == "") || check_format(m_value_temp, m_format);
        }

        return true;
    }

    return false;
}

bool TextBox::keyboard_character_event(unsigned int codepoint) {
    if (m_editable && focused()) {
        std::ostringstream convert;
        convert << (char) codepoint;

        delete_selection();
        m_value_temp.insert(m_cursor_pos, convert.str());
        m_cursor_pos++;

        m_valid_format = (m_value_temp == "") || check_format(m_value_temp, m_format);

        return true;
    }

    return false;
}

bool TextBox::check_format(const std::string &input, const std::string &format)

bool TextBox::copy_selection() {
    if (m_selection_pos > -1) {
        Screen *sc = screen();
        if (!sc)
            return false;

        int begin = m_cursor_pos;
        int end = m_selection_pos;

        if (begin > end)
            std::swap(begin, end);

        glfwSetClipboardString(sc->glfw_window(),
                               m_value_temp.substr(begin, end).c_str());
        return true;
    }

    return false;
}

void TextBox::paste_from_clipboard() {
    Screen *sc = screen();
    if (!sc)
        return;
    const char* cbstr = glfwGetClipboardString(sc->glfw_window());
    if (cbstr)
        m_value_temp.insert(m_cursor_pos, std::string(cbstr));
}

bool TextBox::delete_selection() {
    if (m_selection_pos > -1) {
        int begin = m_cursor_pos;
        int end = m_selection_pos;

        if (begin > end)
            std::swap(begin, end);

        if (begin == end - 1)
            m_value_temp.erase(m_value_temp.begin() + begin);
        else
            m_value_temp.erase(m_value_temp.begin() + begin,
                               m_value_temp.begin() + end);

        m_cursor_pos = begin;
        m_selection_pos = -1;
        return true;
    }

    return false;
}

void TextBox::update_cursor(NVGcontext *, float lastx,
                           const NVGglyphPosition *glyphs, int size) {
    // handle mouse cursor events
    if (m_mouse_down_pos.x() != -1) {
        if (m_mouse_down_modifier == GLFW_MOD_SHIFT) {
            if (m_selection_pos == -1)
                m_selection_pos = m_cursor_pos;
        } else
            m_selection_pos = -1;

        m_cursor_pos =
            position_to_cursor_index(m_mouse_down_pos.x(), lastx, glyphs, size);

        m_mouse_down_pos = Vector2i(-1, -1);
    } else if (m_mouse_drag_pos.x() != -1) {
        if (m_selection_pos == -1)
            m_selection_pos = m_cursor_pos;

        m_cursor_pos =
            position_to_cursor_index(m_mouse_drag_pos.x(), lastx, glyphs, size);
    } else {
        // set cursor to last character
        if (m_cursor_pos == -2)
            m_cursor_pos = size;
    }

    if (m_cursor_pos == m_selection_pos)
        m_selection_pos = -1;
}

float TextBox::cursor_index_to_position(int index, float lastx,
                                        const NVGglyphPosition *glyphs, int size) {
    float pos = 0;
    if (index == size)
        pos = lastx; // last character
    else
        pos = glyphs[index].x;

    return pos;
}

int TextBox::position_to_cursor_index(float posx, float lastx,
                                      const NVGglyphPosition *glyphs, int size) {
    int m_cursor_id = 0;
    float caretx = glyphs[m_cursor_id].x;
    for (int j = 1; j < size; j++) {
        if (std::abs(caretx - posx) > std::abs(glyphs[j].x - posx)) {
            m_cursor_id = j;
            caretx = glyphs[m_cursor_id].x;
        }
    }
    if (std::abs(caretx - posx) > std::abs(lastx - posx))
        m_cursor_id = size;

    return m_cursor_id;
}

TextBox::SpinArea TextBox::spin_area(const Vector2i & pos) {
    if (0 <= pos.x() - m_pos.x() && pos.x() - m_pos.x() < 14.f) { /* on scrolling arrows */
        if (m_size.y() >= pos.y() - m_pos.y() && pos.y() - m_pos.y() <= m_size.y() / 2.f) { /* top part */
            return SpinArea::Top;
        } else if (0.f <= pos.y() - m_pos.y() && pos.y() - m_pos.y() > m_size.y() / 2.f) { /* bottom part */
            return SpinArea::Bottom;
        }
    }
    return SpinArea::None;
}

NAMESPACE_END(nanogui)


try {
        std::regex regex(format);
        return regex_match(input, regex);
    } catch (const std::regex_error &) {
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9)
        std::cerr << "Warning: cannot validate text field due to lacking regular expression support. please compile with GCC >= 4.9" << std::endl;
        return true;
#else
        throw;
#endif
    }

/*
    src/textbox.cpp -- Fancy text box with builtin regular
    expression-based validation

    The text box widget was contributed by Christian Schueller.

    NanoGUI was developed by Wenzel Jakob <example@nnthink.com>.
    The widget drawing code is based on the NanoVG demo application
    by Mikko Mononen.

*/

#include <nanogui/window.h>
#include <nanogui/screen.h>
#include <nanogui/textbox.h>
#include <nanogui/opengl.h>
#include <nanogui/theme.h>
#include <regex>
#include <iostream>

NAMESPACE_BEGIN(nanogui)

TextBox::TextBox(Widget *parent, const std::string &value)
    : Widget(parent),
      m_editable(false),
      m_spinnable(false),
      m_committed(true),
      m_value(value),
      m_default_value(""),
      m_alignment(Alignment::Center),
      m_units(""),
      m_format(""),
      m_units_image(-1),
      m_valid_format(true),
      m_value_temp(value),
      m_cursor_pos(-1),
      m_selection_pos(-1),
      m_mouse_pos(Vector2i(-1,-1)),
      m_mouse_down_pos(Vector2i(-1,-1)),
      m_mouse_drag_pos(Vector2i(-1,-1)),
      m_mouse_down_modifier(0),
      m_text_offset(0),
      m_last_click(0) {
    if (m_theme) m_font_size = m_theme->m_text_box_font_size;
    m_icon_extra_scale = .8f;
}

void TextBox::set_editable(bool editable) {
    m_editable = editable;
    set_cursor(editable ? Cursor::IBeam : Cursor::Arrow);
}

void TextBox::set_theme(Theme *theme) {
    Widget::set_theme(theme);
    if (m_theme)
        m_font_size = m_theme->m_text_box_font_size;
}

Vector2i TextBox::preferred_size(NVGcontext *ctx) const {
    Vector2i size(0, font_size() * 1.4f);

    float uw = 0;
    if (m_units_image > 0) {
        int w, h;
        nvgImageSize(ctx, m_units_image, &w, &h);
        float uh = size[1] * 0.4f;
        uw = w * uh / h;
    } else if (!m_units.empty()) {
        uw = nvgTextBounds(ctx, 0, 0, m_units.c_str(), nullptr, nullptr);
    }
    float sw = 0;
    if (m_spinnable) {
        sw = 14.f;
    }

    float ts = nvgTextBounds(ctx, 0, 0, m_value.c_str(), nullptr, nullptr);
    size[0] = size[1] + ts + uw + sw;
    return size;
}

void TextBox::draw(NVGcontext* ctx) {
    Widget::draw(ctx);

    NVGpaint bg = nvgBoxGradient(ctx,
        m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2, m_size.y() - 2,
        3, 4, Color(255, 32), Color(32, 32));
    NVGpaint fg1 = nvgBoxGradient(ctx,
        m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2, m_size.y() - 2,
        3, 4, Color(150, 32), Color(32, 32));
    NVGpaint fg2 = nvgBoxGradient(ctx,
        m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2, m_size.y() - 2,
        3, 4, nvgRGBA(255, 0, 0, 100), nvgRGBA(255, 0, 0, 50));

    nvgBeginPath(ctx);
    nvgRoundedRect(ctx, m_pos.x() + 1, m_pos.y() + 1 + 1.0f, m_size.x() - 2,
                   m_size.y() - 2, 3);

    if (m_editable && focused())
        m_valid_format ? nvgFillPaint(ctx, fg1) : nvgFillPaint(ctx, fg2);
    else if (m_spinnable && m_mouse_down_pos.x() != -1)
        nvgFillPaint(ctx, fg1);
    else
        nvgFillPaint(ctx, bg);

    nvgFill(ctx);

    nvgBeginPath(ctx);
    nvgRoundedRect(ctx, m_pos.x() + 0.5f, m_pos.y() + 0.5f, m_size.x() - 1,
                   m_size.y() - 1, 2.5f);
    nvgStrokeColor(ctx, Color(0, 48));
    nvgStroke(ctx);

    nvgFontSize(ctx, font_size());
    nvgFontFace(ctx, "sans");
    Vector2i draw_pos(m_pos.x(), m_pos.y() + m_size.y() * 0.5f + 1);

    float x_spacing = m_size.y() * 0.3f;

    float unit_width = 0;

    if (m_units_image > 0) {
        int w, h;
        nvgImageSize(ctx, m_units_image, &w, &h);
        float unit_height = m_size.y() * 0.4f;
        unit_width = w * unit_height / h;
        NVGpaint img_paint = nvgImagePattern(
            ctx, m_pos.x() + m_size.x() - x_spacing - unit_width,
            draw_pos.y() - unit_height * 0.5f, unit_width, unit_height, 0,
            m_units_image, m_enabled ? 0.7f : 0.35f);
        nvgBeginPath(ctx);
        nvgRect(ctx, m_pos.x() + m_size.x() - x_spacing - unit_width,
                draw_pos.y() - unit_height * 0.5f, unit_width, unit_height);
        nvgFillPaint(ctx, img_paint);
        nvgFill(ctx);
        unit_width += 2;
    } else if (!m_units.empty()) {
        unit_width = nvgTextBounds(ctx, 0, 0, m_units.c_str(), nullptr, nullptr);
        nvgFillColor(ctx, Color(255, m_enabled ? 64 : 32));
        nvgTextAlign(ctx, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE);
        nvgText(ctx, m_pos.x() + m_size.x() - x_spacing, draw_pos.y(),
                m_units.c_str(), nullptr);
        unit_width += 2;
    }

    float spin_arrows_width = 0.f;

    if (m_spinnable && !focused()) {
        spin_arrows_width = 14.f;

        nvgFontFace(ctx, "icons");
        nvgFontSize(ctx, ((m_font_size < 0) ? m_theme->m_button_font_size : m_font_size) * icon_scale());

        bool spinning = m_mouse_down_pos.x() != -1;

        /* up button */ {
            bool hover = m_mouse_focus && spin_area(m_mouse_pos) == SpinArea::Top;
            nvgFillColor(ctx, (m_enabled && (hover || spinning)) ? m_theme->m_text_color : m_theme->m_disabled_text_color);
            auto icon = utf8(m_theme->m_text_box_up_icon);
            nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
            Vector2f icon_pos(m_pos.x() + 4.f,
                             m_pos.y() + m_size.y()/2.f - x_spacing/2.f);
            nvgText(ctx, icon_pos.x(), icon_pos.y(), icon.data(), nullptr);
        }

        /* down button */ {
            bool hover = m_mouse_focus && spin_area(m_mouse_pos) == SpinArea::Bottom;
            nvgFillColor(ctx, (m_enabled && (hover || spinning)) ? m_theme->m_text_color : m_theme->m_disabled_text_color);
            auto icon = utf8(m_theme->m_text_box_down_icon);
            nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
            Vector2f icon_pos(m_pos.x() + 4.f,
                             m_pos.y() + m_size.y()/2.f + x_spacing/2.f + 1.5f);
            nvgText(ctx, icon_pos.x(), icon_pos.y(), icon.data(), nullptr);
        }

        nvgFontSize(ctx, font_size());
        nvgFontFace(ctx, "sans");
    }

    switch (m_alignment) {
        case Alignment::Left:
            nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
            draw_pos.x() += x_spacing + spin_arrows_width;
            break;
        case Alignment::Right:
            nvgTextAlign(ctx, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE);
            draw_pos.x() += m_size.x() - unit_width - x_spacing;
            break;
        case Alignment::Center:
            nvgTextAlign(ctx, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
            draw_pos.x() += m_size.x() * 0.5f;
            break;
    }

    nvgFontSize(ctx, font_size());
    nvgFillColor(ctx, m_enabled && (!m_committed || !m_value.empty()) ?
        m_theme->m_text_color :
        m_theme->m_disabled_text_color);

    // clip visible text area
    float clip_x = m_pos.x() + x_spacing + spin_arrows_width - 1.0f;
    float clip_y = m_pos.y() + 1.0f;
    float clip_width = m_size.x() - unit_width - spin_arrows_width - 2 * x_spacing + 2.0f;
    float clip_height = m_size.y() - 3.0f;

    nvgSave(ctx);
    nvgIntersectScissor(ctx, clip_x, clip_y, clip_width, clip_height);

    Vector2i old_draw_pos(draw_pos);
    draw_pos.x() += m_text_offset;

    if (m_committed) {
        nvgText(ctx, draw_pos.x(), draw_pos.y(), m_value.empty() ? m_placeholder.c_str() : m_value.c_str(), nullptr);
    } else {
        const int max_glyphs = 1024;
        NVGglyphPosition glyphs[max_glyphs];
        float text_bound[4];
        nvgTextBounds(ctx, draw_pos.x(), draw_pos.y(), m_value_temp.c_str(),
                      nullptr, text_bound);
        float lineh = text_bound[3] - text_bound[1];

        // find cursor positions
        int nglyphs =
            nvgTextGlyphPositions(ctx, draw_pos.x(), draw_pos.y(),
                                  m_value_temp.c_str(), nullptr, glyphs, max_glyphs);
        update_cursor(ctx, text_bound[2], glyphs, nglyphs);

        // compute text offset
        int prev_cpos = m_cursor_pos > 0 ? m_cursor_pos - 1 : 0;
        int next_cpos = m_cursor_pos < nglyphs ? m_cursor_pos + 1 : nglyphs;
        float prev_cx = cursor_index_to_position(prev_cpos, text_bound[2], glyphs, nglyphs);
        float next_cx = cursor_index_to_position(next_cpos, text_bound[2], glyphs, nglyphs);

        if (next_cx > clip_x + clip_width)
            m_text_offset -= next_cx - (clip_x + clip_width) + 1;
        if (prev_cx < clip_x)
            m_text_offset += clip_x - prev_cx + 1;

        draw_pos.x() = old_draw_pos.x() + m_text_offset;

        // draw text with offset
        nvgText(ctx, draw_pos.x(), draw_pos.y(), m_value_temp.c_str(), nullptr);
        nvgTextBounds(ctx, draw_pos.x(), draw_pos.y(), m_value_temp.c_str(),
                      nullptr, text_bound);

        // recompute cursor positions
        nglyphs = nvgTextGlyphPositions(ctx, draw_pos.x(), draw_pos.y(),
                m_value_temp.c_str(), nullptr, glyphs, max_glyphs);

        if (m_cursor_pos > -1) {
            if (m_selection_pos > -1) {
                float caretx = cursor_index_to_position(m_cursor_pos, text_bound[2],
                                                    glyphs, nglyphs);
                float selx = cursor_index_to_position(m_selection_pos, text_bound[2],
                                                  glyphs, nglyphs);

                if (caretx > selx)
                    std::swap(caretx, selx);

                // draw selection
                nvgBeginPath(ctx);
                nvgFillColor(ctx, nvgRGBA(255, 255, 255, 80));
                nvgRect(ctx, caretx, draw_pos.y() - lineh * 0.5f, selx - caretx,
                        lineh);
                nvgFill(ctx);
            }

            float caretx = cursor_index_to_position(m_cursor_pos, text_bound[2], glyphs, nglyphs);

            // draw cursor
            nvgBeginPath(ctx);
            nvgMoveTo(ctx, caretx, draw_pos.y() - lineh * 0.5f);
            nvgLineTo(ctx, caretx, draw_pos.y() + lineh * 0.5f);
            nvgStrokeColor(ctx, nvgRGBA(255, 192, 0, 255));
            nvgStrokeWidth(ctx, 1.0f);
            nvgStroke(ctx);
        }
    }
    nvgRestore(ctx);
}

bool TextBox::mouse_enter_event(const Vector2i &p, bool enter) {
    Widget::mouse_enter_event(p, enter);
    return true;
}

bool TextBox::mouse_button_event(const Vector2i &p, int button, bool down,
                                 int modifiers) {

    if (button == GLFW_MOUSE_BUTTON_1 && down && !m_focused) {
        if (!m_spinnable || spin_area(p) == SpinArea::None) /* not on scrolling arrows */
            request_focus();
    }

    if (m_editable && focused()) {
        if (down) {
            m_mouse_down_pos = p;
            m_mouse_down_modifier = modifiers;

            double time = glfwGetTime();
            if (time - m_last_click < 0.25) {
                /* Double-click: select all text */
                m_selection_pos = 0;
                m_cursor_pos = (int) m_value_temp.size();
                m_mouse_down_pos = Vector2i(-1, -1);
            }
            m_last_click = time;
        } else {
            m_mouse_down_pos = Vector2i(-1, -1);
            m_mouse_drag_pos = Vector2i(-1, -1);
        }
        return true;
    } else if (m_spinnable && !focused()) {
        if (down) {
            if (spin_area(p) == SpinArea::None) {
                m_mouse_down_pos = p;
                m_mouse_down_modifier = modifiers;

                double time = glfwGetTime();
                if (time - m_last_click < 0.25) {
                    /* Double-click: reset to default value */
                    m_value = m_default_value;
                    if (m_callback)
                        m_callback(m_value);

                    m_mouse_down_pos = Vector2i(-1, -1);
                }
                m_last_click = time;
            } else {
                m_mouse_down_pos = Vector2i(-1, -1);
                m_mouse_drag_pos = Vector2i(-1, -1);
            }
        } else {
            m_mouse_down_pos = Vector2i(-1, -1);
            m_mouse_drag_pos = Vector2i(-1, -1);
        }
        return true;
    }

    return false;
}

bool TextBox::mouse_motion_event(const Vector2i &p, const Vector2i & /* rel */,
                                 int /* button */, int /* modifiers */) {
    m_mouse_pos = p;

    if (!m_editable)
        set_cursor(Cursor::Arrow);
    else if (m_spinnable && !focused() && spin_area(m_mouse_pos) != SpinArea::None) /* scrolling arrows */
        set_cursor(Cursor::Hand);
    else
        set_cursor(Cursor::IBeam);

    return m_editable;
}

bool TextBox::mouse_drag_event(const Vector2i &p, const Vector2i &/* rel */,
                               int /* button */, int /* modifiers */) {
    m_mouse_pos = p;
    m_mouse_drag_pos = p;

    if (m_editable && focused())
        return true;
    return false;
}

bool TextBox::focus_event(bool focused) {
    Widget::focus_event(focused);

    std::string backup = m_value;

    if (m_editable) {
        if (focused) {
            m_value_temp = m_value;
            m_committed = false;
            m_cursor_pos = 0;
        } else {
            if (m_valid_format) {
                if (m_value_temp == "")
                    m_value = m_default_value;
                else
                    m_value = m_value_temp;
            }

            if (m_callback && !m_callback(m_value))
                m_value = backup;

            m_valid_format = true;
            m_committed = true;
            m_cursor_pos = -1;
            m_selection_pos = -1;
            m_text_offset = 0;
        }

        m_valid_format = (m_value_temp == "") || check_format(m_value_temp, m_format);
    }

    return true;
}

bool TextBox::keyboard_event(int key, int /* scancode */, int action, int modifiers) {
    if (m_editable && focused()) {
        if (action == GLFW_PRESS || action == GLFW_REPEAT) {
            if (key == GLFW_KEY_LEFT) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                if (m_cursor_pos > 0)
                    m_cursor_pos--;
            } else if (key == GLFW_KEY_RIGHT) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                if (m_cursor_pos < (int) m_value_temp.length())
                    m_cursor_pos++;
            } else if (key == GLFW_KEY_HOME) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                m_cursor_pos = 0;
            } else if (key == GLFW_KEY_END) {
                if (modifiers == GLFW_MOD_SHIFT) {
                    if (m_selection_pos == -1)
                        m_selection_pos = m_cursor_pos;
                } else {
                    m_selection_pos = -1;
                }

                m_cursor_pos = (int) m_value_temp.size();
            } else if (key == GLFW_KEY_BACKSPACE) {
                if (!delete_selection()) {
                    if (m_cursor_pos > 0) {
                        m_value_temp.erase(m_value_temp.begin() + m_cursor_pos - 1);
                        m_cursor_pos--;
                    }
                }
            } else if (key == GLFW_KEY_DELETE) {
                if (!delete_selection()) {
                    if (m_cursor_pos < (int) m_value_temp.length())
                        m_value_temp.erase(m_value_temp.begin() + m_cursor_pos);
                }
            } else if (key == GLFW_KEY_ENTER) {
                if (!m_committed)
                    focus_event(false);
            } else if (key == GLFW_KEY_A && modifiers == SYSTEM_COMMAND_MOD) {
                m_cursor_pos = (int) m_value_temp.length();
                m_selection_pos = 0;
            } else if (key == GLFW_KEY_X && modifiers == SYSTEM_COMMAND_MOD) {
                copy_selection();
                delete_selection();
            } else if (key == GLFW_KEY_C && modifiers == SYSTEM_COMMAND_MOD) {
                copy_selection();
            } else if (key == GLFW_KEY_V && modifiers == SYSTEM_COMMAND_MOD) {
                delete_selection();
                paste_from_clipboard();
            }

            m_valid_format =
                (m_value_temp == "") || check_format(m_value_temp, m_format);
        }

        return true;
    }

    return false;
}

bool TextBox::keyboard_character_event(unsigned int codepoint) {
    if (m_editable && focused()) {
        std::ostringstream convert;
        convert << (char) codepoint;

        delete_selection();
        m_value_temp.insert(m_cursor_pos, convert.str());
        m_cursor_pos++;

        m_valid_format = (m_value_temp == "") || check_format(m_value_temp, m_format);

        return true;
    }

    return false;
}

bool TextBox::check_format(const std::string &input, const std::string &format) {
    if (format.empty())
        return true;

}

bool TextBox::copy_selection() {
    if (m_selection_pos > -1) {
        Screen *sc = screen();
        if (!sc)
            return false;

        int begin = m_cursor_pos;
        int end = m_selection_pos;

        if (begin > end)
            std::swap(begin, end);

        glfwSetClipboardString(sc->glfw_window(),
                               m_value_temp.substr(begin, end).c_str());
        return true;
    }

    return false;
}

void TextBox::paste_from_clipboard() {
    Screen *sc = screen();
    if (!sc)
        return;
    const char* cbstr = glfwGetClipboardString(sc->glfw_window());
    if (cbstr)
        m_value_temp.insert(m_cursor_pos, std::string(cbstr));
}

bool TextBox::delete_selection() {
    if (m_selection_pos > -1) {
        int begin = m_cursor_pos;
        int end = m_selection_pos;

        if (begin > end)
            std::swap(begin, end);

        if (begin == end - 1)
            m_value_temp.erase(m_value_temp.begin() + begin);
        else
            m_value_temp.erase(m_value_temp.begin() + begin,
                               m_value_temp.begin() + end);

        m_cursor_pos = begin;
        m_selection_pos = -1;
        return true;
    }

    return false;
}

void TextBox::update_cursor(NVGcontext *, float lastx,
                           const NVGglyphPosition *glyphs, int size) {
    // handle mouse cursor events
    if (m_mouse_down_pos.x() != -1) {
        if (m_mouse_down_modifier == GLFW_MOD_SHIFT) {
            if (m_selection_pos == -1)
                m_selection_pos = m_cursor_pos;
        } else
            m_selection_pos = -1;

        m_cursor_pos =
            position_to_cursor_index(m_mouse_down_pos.x(), lastx, glyphs, size);

        m_mouse_down_pos = Vector2i(-1, -1);
    } else if (m_mouse_drag_pos.x() != -1) {
        if (m_selection_pos == -1)
            m_selection_pos = m_cursor_pos;

        m_cursor_pos =
            position_to_cursor_index(m_mouse_drag_pos.x(), lastx, glyphs, size);
    } else {
        // set cursor to last character
        if (m_cursor_pos == -2)
            m_cursor_pos = size;
    }

    if (m_cursor_pos == m_selection_pos)
        m_selection_pos = -1;
}

float TextBox::cursor_index_to_position(int index, float lastx,
                                        const NVGglyphPosition *glyphs, int size) {
    float pos = 0;
    if (index == size)
        pos = lastx; // last character
    else
        pos = glyphs[index].x;

    return pos;
}

int TextBox::position_to_cursor_index(float posx, float lastx,
                                      const NVGglyphPosition *glyphs, int size) {
    int m_cursor_id = 0;
    float caretx = glyphs[m_cursor_id].x;
    for (int j = 1; j < size; j++) {
        if (std::abs(caretx - posx) > std::abs(glyphs[j].x - posx)) {
            m_cursor_id = j;
            caretx = glyphs[m_cursor_id].x;
        }
    }
    if (std::abs(caretx - posx) > std::abs(lastx - posx))
        m_cursor_id = size;

    return m_cursor_id;
}

TextBox::SpinArea TextBox::spin_area(const Vector2i & pos) {
    if (0 <= pos.x() - m_pos.x() && pos.x() - m_pos.x() < 14.f) { /* on scrolling arrows */
        if (m_size.y() >= pos.y() - m_pos.y() && pos.y() - m_pos.y() <= m_size.y() / 2.f) { /* top part */
            return SpinArea::Top;
        } else if (0.f <= pos.y() - m_pos.y() && pos.y() - m_pos.y() > m_size.y() / 2.f) { /* bottom part */
            return SpinArea::Bottom;
        }
    }
    return SpinArea::None;
}

NAMESPACE_END(nanogui)


/*
//@HEADER
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// Questions Contact Brian Kelley (example@nnthink.com)
//@HEADER
*/

// EXERCISE (scroll down to main() on line 157)
//   - Goal: Run Distance-1 and Distance-2 graph coloring on a small rectangular grid.

#include <vector>
#include <cstdio>
#include <cmath>
#include <sstream>
#include "Kokkos_Core.hpp"
#include "KokkosKernels_default_types.hpp"
#include "KokkosKernels_Handle.hpp"
#include "KokkosGraph_Distance1Color.hpp"
#include "KokkosGraph_Distance2Color.hpp"

//Typedefs

using Ordinal = default_lno_t;
using Offset  = default_size_type;
using Layout  = default_layout;
using ExecSpace = Kokkos::DefaultExecutionSpace;
using DeviceSpace = typename ExecSpace::memory_space;
using Kokkos::HostSpace;
using RowmapType = Kokkos::View<Offset*, DeviceSpace>;
using ColindsType = Kokkos::View<Ordinal*, DeviceSpace>;
using Handle  = KokkosKernels::Experimental::
  KokkosKernelsHandle<Offset, Ordinal, default_scalar, ExecSpace, DeviceSpace, DeviceSpace>;

namespace ColoringExercise
{
  constexpr Ordinal gridX = 15;
  constexpr Ordinal gridY = 25;
  constexpr Ordinal numVertices = gridX * gridY;

  //Helper to get the vertex ID given grid coordinates
  Ordinal getVertexID(Ordinal x, Ordinal y)

  //Inverse of getVertexID
  void getVertexPos(Ordinal vert, Ordinal& x, Ordinal& y)
  {
    x = vert % gridX;
    y = vert / gridX;
  }

  //Helper to print out colors in the shape of the grid
  template<typename ColorView>
  void printColoring(ColorView colors, Ordinal numColors)
  {
    //Read colors on host
    auto colorsHost = Kokkos::create_mirror_view_and_copy(HostSpace(), colors);
    int numDigits = ceil(log10(numColors + 1));
    //Print out the grid, with columns aligned and at least one space between numbers
    std::ostringstream numFmtStream;
    numFmtStream << '%' << numDigits + 1 << 'd';
    std::string numFmt = numFmtStream.str();
    for(Ordinal y = 0; y < gridY; y++)
    {
      for(Ordinal x = 0; x < gridX; x++)
      {
        Ordinal vertex = getVertexID(x, y);
        int color = colorsHost(vertex);
        printf(numFmt.c_str(), color);
      }
      putchar('\n');
    }
  }

  //Build the graph on host, allocate these views on device and copy the graph to them.
  //Both rowmapDevice and colindsDevice are output parameters and should default-initialized (empty) on input.
  void generate9pt(RowmapType& rowmapDevice, ColindsType& colindsDevice)
  {
    //Generate the graph on host (use std::vector to not need to know
    //how many entries ahead of time)
    std::vector<Offset> rowmap(numVertices + 1);
    std::vector<Ordinal> colinds;
    rowmap[0] = 0;
    for(Ordinal vert = 0; vert < numVertices; vert++)
    {
      Ordinal x, y;
      getVertexPos(vert, x, y);
      //Loop over the neighbors in a 3x3 region
      for(Ordinal ny = y - 1; ny <= y + 1; ny++)
      {
        for(Ordinal nx = x - 1; nx <= x + 1; nx++)
        {
          //exclude the edge to self
          if(nx == x && ny == y)
            continue;
          //exclude vertices that would be outside the grid
          if(nx < 0 || nx >= gridX || ny < 0 || ny >= gridY)
            continue;
          //add the neighbor to colinds, forming an edge
          colinds.push_back(getVertexID(nx, ny));
        }
      }
      //mark where the current row ends
      rowmap[vert + 1] = colinds.size();
    }
    Offset numEdges = colinds.size();
    //Now that the graph is formed, copy rowmap and colinds to Kokkos::Views in device memory
    //The nonowning host views just alias the std::vectors.
    Kokkos::View<Offset*, HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> rowmapHost(rowmap.data(), numVertices + 1);
    Kokkos::View<Ordinal*, HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> colindsHost(colinds.data(), numEdges);
    //Allocate owning views on device with the correct size.
    rowmapDevice = RowmapType("Rowmap", numVertices + 1);
    colindsDevice = ColindsType("Colinds", numEdges);
    //Copy the graph from host to device
    Kokkos::deep_copy(rowmapDevice, rowmapHost);
    Kokkos::deep_copy(colindsDevice, colindsHost);
  }
}

int main(int argc, char* argv[])
{
  Kokkos::initialize();
  {
    using ColoringExercise::numVertices;
    RowmapType rowmapDevice;
    ColindsType colindsDevice;
    //Step 1: Generate the graph on host, then allocate space and copy to device.
    ColoringExercise::generate9pt(rowmapDevice, colindsDevice);
    //Step 2: Create handle and run distance-1 coloring.
    {
      using D1_ColoringHandleType = typename Handle::GraphColoringHandleType;
      using color_view_t = typename D1_ColoringHandleType::color_view_t;
      //General handle
      Handle handle;
//- EXERCISE: Create distance-1 graph coloring subhandle, with the default algorithm

//- EXERCISE: Run coloring on the graph (rowmapDevice, colindsDevice), which has numVertices rows/columns

      D1_ColoringHandleType* colorHandle = handle.get_graph_coloring_handle();
//- EXERCISE: From colorHandle, get the color array and the number of colors.
      color_view_t colors /* = colorHandle-> ... */;
      Ordinal numColors /* = colorHandle-> ... */;
      printf("9-pt stencil: Distance-1 Colors (used %d):\n", (int) numColors);
      ColoringExercise::printColoring(colors, numColors);
      putchar('\n');
//- EXERCISE: Clean up the distance-1 graph coloring subhandle

    }
    //Step 3: Create handle and run distance-2 coloring.
    {
      using D2_ColoringHandleType = typename Handle::GraphColorDistance2HandleType;
      using color_view_t = typename D2_ColoringHandleType::color_view_type;
      //General handle
      Handle handle;
//- EXERCISE: Create distance-2 graph coloring subhandle, with the default algorithm

//- EXERCISE: Run D2 coloring on the graph (rowmapDevice, colindsDevice), which has numVertices vertices.

      D2_ColoringHandleType* colorHandleD2 = handle.get_distance2_graph_coloring_handle();
//- EXERCISE: From colorHandle, get the color array and the number of colors.
      color_view_t colors /* = colorHandleD2-> ... */;
      Ordinal numColors /* = colorHandleD2-> ... */;
      printf("9-pt stencil: Distance-2 Colors (used %d):\n", (int) numColors);
      ColoringExercise::printColoring(colors, numColors);
      putchar('\n');
//- EXERCISE: Clean up the distance-2 graph coloring subhandle

    }
  }
  Kokkos::finalize();
  return 0;
}


Ordinal getVertexID(Ordinal x, Ordinal y)
  {
    return y * gridX + x;
  }

/*
//@HEADER
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// Questions Contact Brian Kelley (example@nnthink.com)
//@HEADER
*/

// EXERCISE (scroll down to main() on line 157)
//   - Goal: Run Distance-1 and Distance-2 graph coloring on a small rectangular grid.

#include <vector>
#include <cstdio>
#include <cmath>
#include <sstream>
#include "Kokkos_Core.hpp"
#include "KokkosKernels_default_types.hpp"
#include "KokkosKernels_Handle.hpp"
#include "KokkosGraph_Distance1Color.hpp"
#include "KokkosGraph_Distance2Color.hpp"

//Typedefs

using Ordinal = default_lno_t;
using Offset  = default_size_type;
using Layout  = default_layout;
using ExecSpace = Kokkos::DefaultExecutionSpace;
using DeviceSpace = typename ExecSpace::memory_space;
using Kokkos::HostSpace;
using RowmapType = Kokkos::View<Offset*, DeviceSpace>;
using ColindsType = Kokkos::View<Ordinal*, DeviceSpace>;
using Handle  = KokkosKernels::Experimental::
  KokkosKernelsHandle<Offset, Ordinal, default_scalar, ExecSpace, DeviceSpace, DeviceSpace>;

namespace ColoringExercise
{
  constexpr Ordinal gridX = 15;
  constexpr Ordinal gridY = 25;
  constexpr Ordinal numVertices = gridX * gridY;

  //Helper to get the vertex ID given grid coordinates


/*  This file is part of Winyl Player source code.

*/

#include "stdafx.h"
#include "SkinSwitch.h"

SkinSwitch::SkinSwitch()
{

}

SkinSwitch::~SkinSwitch()

bool SkinSwitch::LoadSkin(const std::wstring& file, ZipFile* zipFile)
{
	std::wstring path = PathEx::PathFromFile(file);

	XmlFile xmlFile;

	if (xmlFile.LoadEx(file, zipFile))
	{
		XmlNode xmlMain = xmlFile.RootNode().FirstChild("Switch");

		if (xmlMain)
		{
			XmlNode xmlState1 = xmlMain.FirstChild("State1");

			if (xmlState1)
			{
				XmlNode xmlNormal = xmlState1.FirstChild("Normal");
				if (xmlNormal)
				{
					std::wstring attr = xmlNormal.Attribute16("File");
					if (!attr.empty())
						imButton[0].LoadEx(path + attr, zipFile);
				}

				XmlNode xmlHover = xmlState1.FirstChild("Hover");
				if (xmlHover)
				{
					std::wstring attr = xmlHover.Attribute16("File");
					if (!attr.empty())
						imButton[1].LoadEx(path + attr, zipFile);
				}

				XmlNode xmlPress = xmlState1.FirstChild("Press");
				if (xmlPress)
				{
					std::wstring attr = xmlPress.Attribute16("File");
					if (!attr.empty())
						imButton[2].LoadEx(path + attr, zipFile);
				}
			}

			XmlNode xmlState2 = xmlMain.FirstChild("State2");

			if (xmlState2)
			{
				XmlNode xmlNormal = xmlState2.FirstChild("Normal");
				if (xmlNormal)
				{
					std::wstring attr = xmlNormal.Attribute16("File");
					if (!attr.empty())
						imButton2[0].LoadEx(path + attr, zipFile);
				}

				XmlNode xmlHover = xmlState2.FirstChild("Hover");
				if (xmlHover)
				{
					std::wstring attr = xmlHover.Attribute16("File");
					if (!attr.empty())
						imButton2[1].LoadEx(path + attr, zipFile);
				}

				XmlNode xmlPress = xmlState2.FirstChild("Press");
				if (xmlPress)
				{
					std::wstring attr = xmlPress.Attribute16("File");
					if (!attr.empty())
						imButton2[2].LoadEx(path + attr, zipFile);
				}
			}

			XmlNode xmlEffect = xmlMain.FirstChild("Effect");
			if (xmlEffect)
				xmlEffect.Attribute("Fade", &isFadeEffect);
		}
	}
	else
		return false;

	return true;
}

void SkinSwitch::Draw(HDC dc, bool isAlpha)
{
	if (!isState)
	{
		if (!isHover)
			imButton[0].Draw(dc, rcRect);
		else if (isPress)
		{
			if (imButton[2].IsValid())
				imButton[2].Draw(dc, rcRect);
			else if (imButton[1].IsValid())
				imButton[1].Draw(dc, rcRect);
			else
				imButton[0].Draw(dc, rcRect);
		}
		else if (isHover)
		{
			if (imButton[1].IsValid())
				imButton[1].Draw(dc, rcRect);
			else
				imButton[0].Draw(dc, rcRect);
		}
	}
	else
	{
		if (!isHover)
			imButton2[0].Draw(dc, rcRect);
		else if (isPress)
		{
			if (imButton2[2].IsValid())
				imButton2[2].Draw(dc, rcRect);
			else if (imButton2[1].IsValid())
				imButton2[1].Draw(dc, rcRect);
			else
				imButton2[0].Draw(dc, rcRect);
		}
		else if (isHover)
		{
			if (imButton2[1].IsValid())
				imButton2[1].Draw(dc, rcRect);
			else
				imButton2[0].Draw(dc, rcRect);
		}
	}
}

bool SkinSwitch::DrawFade(HDC dc, bool isAlpha)
{
	if (isPress)
	{
		if (!isState)
		{
			if (imButton[2].IsValid())
				imButton[2].Draw(dc, rcRect);
			else
				imButton[1].Draw(dc, rcRect);
		}
		else
		{
			if (imButton2[2].IsValid())
				imButton2[2].Draw(dc, rcRect);
			else
				imButton2[1].Draw(dc, rcRect);
		}

		fadeAlpha = 255;
		return true;
	}

	if (!isState)
	{
		if (!isHover)
		{
			fadeAlpha -= FadeValue::StepOut;
			fadeAlpha = std::max(0, fadeAlpha);

			if (fadeAlpha == 0)
				imButton[0].Draw(dc, rcRect);
			else
			{
				imButton[0].DrawFade(dc, rcRect, 255 - fadeAlpha);
				imButton[1].DrawFade(dc, rcRect, fadeAlpha);
			}
		}
		else if (isHover)
		{
			fadeAlpha += FadeValue::StepIn;
			fadeAlpha = std::min(255, fadeAlpha);

			if (fadeAlpha == 255)
				imButton[1].Draw(dc, rcRect);
			else
			{
				imButton[0].DrawFade(dc, rcRect, 255 - fadeAlpha);
				imButton[1].DrawFade(dc, rcRect, fadeAlpha);
			}
		}
	}
	else
	{
		if (!isHover)
		{
			fadeAlpha -= FadeValue::StepOut;
			fadeAlpha = std::max(0, fadeAlpha);

			if (fadeAlpha == 0)
				imButton2[0].Draw(dc, rcRect);
			else
			{
				imButton2[0].DrawFade(dc, rcRect, 255 - fadeAlpha);
				imButton2[1].DrawFade(dc, rcRect, fadeAlpha);
			}
		}
		else if (isHover)
		{
			fadeAlpha += FadeValue::StepIn;
			fadeAlpha = std::min(255, fadeAlpha);

			if (fadeAlpha == 255)
				imButton2[1].Draw(dc, rcRect);
			else
			{
				imButton2[0].DrawFade(dc, rcRect, 255 - fadeAlpha);
				imButton2[1].DrawFade(dc, rcRect, fadeAlpha);
			}
		}
	}

	if (fadeAlpha == 0 || fadeAlpha == 255)
		return true;
	else
		return false;
}

int SkinSwitch::GetWidth()
{
	return imButton[0].Width();
}

int SkinSwitch::GetHeight()
{
	return imButton[0].Height();
}

bool SkinSwitch::IsRedrawHover()
{
	if (isPress)
	{
		if (!isState)
		{
			if (imButton[2].IsValid())
				return true;
			if (imButton[1].IsValid())
				return true;
		}
		else
		{
			if (imButton2[2].IsValid())
				return true;
			if (imButton2[1].IsValid())
				return true;
		}

		return false;
	}

	if (!isState)
	{
		if (imButton[1].IsValid())
			return true;
	}
	else
	{
		if (imButton2[1].IsValid())
			return true;
	}

	return false;
}

bool SkinSwitch::IsRedrawPress()
{
	if (!isState)
	{
		if (imButton[2].IsValid())
			return true;
	}
	else
	{
		if (imButton2[2].IsValid())
			return true;
	}

	return false;
}

SkinElement* SkinSwitch::OnMouseMove(unsigned flags, CPoint& point)
{
	if (!isHover && rcRect.PtInRect(point)) // Mouse enters button area
	{

		isHover = true;
		return this;
	}
	else if (isHover && !rcRect.PtInRect(point)) // Mouse leaves button area
	{
		isHover = false;
		return this;
	}

	return nullptr;
}

SkinElement* SkinSwitch::OnButtonDown(unsigned flags, CPoint& point)
{
	if (rcRect.PtInRect(point))
	{
		isPress = true;
		return this;
	}

	return nullptr;
}

SkinElement* SkinSwitch::OnButtonUp(unsigned flags, CPoint& point)
{
	if (isPress)
	{
		if (!isHover)
			fadeAlpha = 0;

		isPress = false;
		return this;
	}

	return nullptr;
}

SkinElement* SkinSwitch::OnMouseLeave()
{
	if (isHover)
	{
		isHover = false;
		return this;
	}

	return nullptr;
}

int SkinSwitch::GetParam()
{
	return (int)isState;
}

void SkinSwitch::SetParam(int param)
{
	isState = (param ? true : false);
}