/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "EmojiFactory.h"

#define LOG_TAG "EmojiFactory"
#include <utils/Log.h>
#include <utils/Vector.h>

#include <cutils/properties.h>

#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>


namespace android {

static pthread_once_t g_once = PTHREAD_ONCE_INIT;
static Vector<EmojiFactory *> *g_factories = NULL;
static Vector<void *> *g_handles = NULL;

class EmojiFactoryManager {
 public:
  void Init();
  virtual ~EmojiFactoryManager();
 private:
  void TryRegisterEmojiFactory(const char *library_name);
};

// Note: I previously did this procedure in the construcor. However,
// property_get() didn't return a correct value in that context. I guess
// property_get() does not return correct values before AndroidRuntime
// instance (or exactly, AppRuntime in instance app_main.cpp) is
// fully ready (see AndroidRunitem.cpp and app_main.cpp).
// So, instead of doing this in constructor, I decided this shoud be done
// when a user requires to EmojiFactory, which makes better sense to me.
void EmojiFactoryManager::Init() {
  g_handles = new Vector<void *>();
  g_factories = new Vector<EmojiFactory *>();

  char *emoji_libraries = new char[PROPERTY_VALUE_MAX];
  int len = property_get("ro.config.libemoji", emoji_libraries, "");
  // LOGD("ro.config.libemoji: %s", emoji_libraries);
  if (len > 0) {
    char *saveptr, *ptr;
    ptr = emoji_libraries;
    while (true) {
      ptr = strtok_r(ptr, ":", &saveptr);
      if (NULL == ptr) {
        break;
      }
      TryRegisterEmojiFactory(ptr);
      ptr = NULL;
    }
  }

  delete [] emoji_libraries;
}

void EmojiFactoryManager::TryRegisterEmojiFactory(const char *library_name) {
  void *handle = dlopen(library_name, RTLD_LAZY | RTLD_LOCAL);
  if (handle == NULL) {
    const char* error_str = dlerror();
    if (error_str) {
      error_str = "Unknown reason";
    }
    LOGE("Failed to load shared library %s: %s", library_name, error_str);
    return;
  }
  EmojiFactory *(*get_emoji_factory)() =
      reinterpret_cast<EmojiFactory *(*)()>(dlsym(handle,
                                                  "GetEmojiFactory"));
  if (get_emoji_factory == NULL) {
    const char* error_str = dlerror();
    if (error_str) {
      error_str = "Unknown reason";
    }
    LOGE("Failed to call GetEmojiFactory: %s", error_str);
    dlclose(handle);
    return;
  }

  EmojiFactory *factory = (*get_emoji_factory)();
  if (NULL == factory) {
    LOGE("Returned factory is NULL");
    dlclose(handle);
    return;
  }

  const char *name = factory->Name();

  size_t size = g_factories->size();
  for (size_t i = 0; i < size; ++i) {
    EmojiFactory *f = g_factories->itemAt(i);
    if (!strcmp(name, f->Name())) {
      LOGE("Same EmojiFactory was found: %s", name);
      delete factory;
      dlclose(handle);
      return;
    }
  }
  g_factories->push(factory);
  // dlclose() must not be called here, since returned factory may point to
  // static data in the shared library (like "static const char* = "emoji";")
  g_handles->push(handle);
}

EmojiFactoryManager::~EmojiFactoryManager() {
  if (g_factories != NULL) {
    size_t size = g_factories->size();
    for (size_t i = 0; i < size; ++i) {
      delete g_factories->itemAt(i);
    }
    delete g_factories;
  }

  if (g_handles != NULL) {
    size_t size = g_handles->size();
    for (size_t i = 0; i < size; ++i) {
      dlclose(g_handles->itemAt(i));
    }
    delete g_handles;
  }
}

static EmojiFactoryManager g_registrar;

static void InitializeEmojiFactory() {
  g_registrar.Init();
}

/* static */
EmojiFactory *EmojiFactory::GetImplementation(const char *name) {
  pthread_once(&g_once, InitializeEmojiFactory);
  if (NULL == name) {
    return NULL;
  }
  size_t size = g_factories->size();
  for (size_t i = 0; i < size; ++i) {
    EmojiFactory *factory = g_factories->itemAt(i);
    if (!strcmp(name, factory->Name())) {
      return factory;
    }
  }
  return NULL;
}

/* static */
EmojiFactory *EmojiFactory::GetAvailableImplementation() {
  pthread_once(&g_once, InitializeEmojiFactory);
  size_t size = g_factories->size();
  for (size_t i = 0; i < size; ++i) {
    EmojiFactory *factory = g_factories->itemAt(i);
    return factory;
  }
  return NULL;
}

}  // namespace android

extern "C" android::EmojiFactory *GetImplementation(
    const char *name) {
  return android::EmojiFactory::GetImplementation(name);
}

extern "C" android::EmojiFactory *GetAvailableImplementation() {
  return android::EmojiFactory::GetAvailableImplementation();
}