/*******************************************************************************
 * Copyright (c) 2012-2015 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.api.local;

import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.user.server.dao.Profile;
import org.eclipse.che.api.user.server.dao.UserProfileDao;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Singleton
public class LocalProfileDaoImpl implements UserProfileDao {
    private static final Logger LOG = LoggerFactory.getLogger(LocalProfileDaoImpl.class);

    private final File                 storageFile;
    private final Gson                 gson;
    private final Map<String, Profile> profiles;
    private final ReadWriteLock        lock;

    @Inject
    public LocalProfileDaoImpl(@Nullable @Named("profile.store_location") String dirPath) {
        if (dirPath == null || dirPath.isEmpty()) {
            storageFile = new File(System.getProperty("java.io.tmpdir"), "ProfileStorage.json");
        } else {
            storageFile = new File(dirPath, "ProfileStorage.json");
        }
        gson = new Gson();
        profiles = new HashMap<>();
        lock = new ReentrantReadWriteLock();
    }

    @PostConstruct
    private void start() {
        // use write lock since we are validate storage at this stage
        lock.writeLock().lock();
        try {
            if (storageFile.exists()) {
                Reader reader = null;
                try {
                    reader = Files.newReader(storageFile, Charset.forName("UTF-8"));
                    Map<String, Profile> m = gson.fromJson(reader, new TypeToken<Map<String, Profile>>() {
                    }.getType());
                    if (m != null) {
                        profiles.putAll(m);
                    }
                } catch (Exception e) {
                    LOG.error(String.format("Failed load user profiles form %s", storageFile), e);
                } finally {
                    if (reader != null) {
                        try {
                            reader.close();
                        } catch (IOException ignored) {
                        }
                    }
                }
            }
            // Add default entry if file doesn't exist or invalid or empty.
            if (profiles.isEmpty()) {
                final Map<String, String> attributes = new HashMap<>(2);
                attributes.put("First Name", "Codenvy");
                attributes.put("Last Name", "Codenvy");
                Profile profile = new Profile().withId("codenvy")
                                               .withUserId("codenvy")
                                               .withAttributes(attributes);
                profiles.put(profile.getId(), profile);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    @PreDestroy
    private void stop() {
        lock.writeLock().lock();
        try {
            Writer writer = null;
            try {
                writer = Files.newWriter(storageFile, Charset.forName("UTF-8"));
                gson.toJson(profiles, new TypeToken<Map<String, Profile>>() {
                }.getType(), writer);
            } catch (Exception e) {
                LOG.error(String.format("Failed setPreferences user profiles form %s", storageFile), e);
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        LOG.error(String.format("Failed setPreferences user profiles form %s", storageFile), e);
                    }
                }
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public void create(Profile profile) {
        lock.writeLock().lock();
        try {
            // just replace existed profile
            final Profile copy = new Profile().withId(profile.getId()).withUserId(profile.getUserId())
                                              .withAttributes(new LinkedHashMap<>(profile.getAttributes()));
            profiles.put(copy.getId(), copy);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public void update(Profile profile) throws NotFoundException {
        lock.writeLock().lock();
        try {
            final Profile myProfile = profiles.get(profile.getId());
            if (myProfile == null) {
                throw new NotFoundException(String.format("Profile not found %s", profile.getId()));
            }
            myProfile.getAttributes().clear();
            myProfile.getAttributes().putAll(profile.getAttributes());
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public void remove(String id) throws NotFoundException {
        lock.writeLock().lock();
        try {
            final Profile profile = profiles.remove(id);
            if (profile == null) {
                throw new NotFoundException(String.format("Profile not found %s", id));
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public Profile getById(String id) throws NotFoundException {
        lock.readLock().lock();
        try {
            final Profile profile = profiles.get(id);
            if (profile == null) {
                throw new NotFoundException(String.format("Profile not found %s", id));
            }
            return new Profile().withId(profile.getId()).withUserId(profile.getUserId())
                                .withAttributes(new LinkedHashMap<>(profile.getAttributes()));
        } finally {
            lock.readLock().unlock();
        }
    }
}

@@ -3441,14 +3441,13 @@
 ut(%22
-F
+f
 irst
- 
 Name
@@ -3443,38 +3443,34 @@
 (%22firstName%22, %22C
-odenvy
+he
 %22);%0A            
@@ -3493,13 +3493,12 @@
 ut(%22
-L
+l
 ast
- 
 Name

Fix attrbute name for user profile First Name -> firstName and Last Name -> lastName