package org.commcare.model.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import java.io.InputStream;

import org.commcare.resources.model.Resource;
import org.commcare.resources.model.ResourceTable;
import org.commcare.suite.model.Profile;
import org.commcare.test.utils.PersistableSandbox;
import org.commcare.util.CommCareConfigEngine;
import org.commcare.xml.ProfileParser;
import org.javarosa.core.services.storage.util.DummyIndexedStorageUtility;
import org.javarosa.core.util.ArrayUtilities;
import org.junit.Before;
import org.junit.Test;

/**
 * Regressions and unit tests for the profile model.
 * 
 * Covers functioning specific to the profile itself, not
 * to setup/usage of resource tables or other models.
 * 
 * @author ctsims
 */
public class ProfileTests {
    private PersistableSandbox mSandbox;
    private CommCareConfigEngine mAppPlatform;
    private ResourceTable mFreshResourceTable;
    
    @Before
    public void setUp() {
        mSandbox = new PersistableSandbox();
        mAppPlatform = new CommCareConfigEngine();
        mFreshResourceTable = ResourceTable.RetrieveTable(
                new DummyIndexedStorageUtility(ResourceTable.class));
    }
    
    @Test
    public void testProfileParse() {
        Profile p = getBasicProfile();
        assertEquals("Profile is not set to the correct version: (102)", p.getVersion(), 102);
    }
    
    @Test
    public void testBasicProfileSerialization() {
        Profile p = getBasicProfile();
        byte[] serializedProfile = mSandbox.serialize(p);
        
        Profile deserialized = mSandbox.deserialize(serializedProfile, Profile.class);
        
        //Maybe this should just be p.equals(deserialized)? Kind of hard to say with deep
        //models of this type.
        compareProfiles(p, deserialized);
    }

    @Test
    public void testMultipleAppsProfileSerialization() {
        Profile p = getMultipleAppsProfile();
        byte[] serializedProfile = mSandbox.serialize(p);

        Profile deserialized = mSandbox.deserialize(serializedProfile, Profile.class);

        compareProfiles(p, deserialized);
    }

    // Tests that a profile.ccpr which was missing the necessary fields for multiple apps has
    // them generated correctly by the parser
    @Test
    public void testGeneratedProfileFields() {
        Profile p = getBasicProfile();

        assertNotNull("Profile uniqueId was null", p.getUniqueId());
        assertNotNull("Profile display name was null", p.getDisplayName());
    }

    private void compareProfiles(Profile a, Profile b) {
        if(!ArrayUtilities.arraysEqual(a.getPropertySetters(), b.getPropertySetters())) {
            fail("Mismatch of property setters between profiles");
        }

        assertEquals("Mismatched auth references", a.getAuthReference(), b.getAuthReference());

        assertEquals("Mismatched profile versions", a.getVersion(), b.getVersion());

        assertEquals("Mismatched profile unique ids", a.getUniqueId(), b.getUniqueId());

        assertEquals("Mismatched profile display names", a.getDisplayName(), b.getDisplayName());

        assertEquals("Mismatched profiles on old version", a.isOldVersion(), b.isOldVersion());

        //TODO: compare root references and other mismatched fields
    }

    private Profile getBasicProfile() {
        try{
            String basicProfilePath = "/basic_profile.ccpr";
            InputStream is = this.getClass().getResourceAsStream(basicProfilePath);
            if(is == null) {
                throw new RuntimeException("Test resource missing: " + basicProfilePath);
            }
            
            ProfileParser parser = new ProfileParser(is, mAppPlatform.getInstance(), mFreshResourceTable, "profile", 
                    Resource.RESOURCE_VERSION_UNKNOWN, false);
            
            return parser.parse();
        } catch(Exception e) {
            throw PersistableSandbox.wrap("Error during profile test setup", e);
        }
    }

    // Return a Profile object constructed from a profile.ccpr file explicitly containing the
    // fields necessary for multiple apps support
    private Profile getMultipleAppsProfile() {
        try{
            String basicProfilePath = "/multiple_apps_profile.ccpr";
            InputStream is = this.getClass().getResourceAsStream(basicProfilePath);
            if(is == null) {
                throw new RuntimeException("Test resource missing: " + basicProfilePath);
            }

            ProfileParser parser = new ProfileParser(is, mAppPlatform.getInstance(), mFreshResourceTable, "profile",
                    Resource.RESOURCE_VERSION_UNKNOWN, false);

            return parser.parse();
        } catch(Exception e) {
            throw PersistableSandbox.wrap("Error during profile test setup", e);
        }
    }
}

@@ -992,20 +992,182 @@
 eTable;%0A
+%0A
     
+private final static String BASIC_PROFILE_PATH = %22/basic_profile.ccpr%22;%0A    private final static String MULT_APPS_PROFILE_PATH = %22/multiple_apps_profile.ccpr%22;%0A%0A
 %0A    @Be
@@ -1499,37 +1499,32 @@
 file p = get
-Basic
 Profile(
 );%0A        a
@@ -1503,32 +1503,50 @@
  p = getProfile(
+BASIC_PROFILE_PATH
 );%0A        asser
@@ -1721,29 +1721,24 @@
  p = get
-Basic
 Profile(
 );%0A     
@@ -1721,32 +1721,50 @@
  p = getProfile(
+BASIC_PROFILE_PATH
 );%0A        byte%5B
@@ -2173,36 +2173,46 @@
  p = get
-MultipleAppsProfile(
+Profile(MULT_APPS_PROFILE_PATH
 );%0A     
@@ -2264,17 +2264,16 @@
 ize(p);%0A
-%0A
         
@@ -2351,17 +2351,16 @@
 class);%0A
-%0A
         
@@ -2620,29 +2620,24 @@
  p = get
-Basic
 Profile(
 );%0A%0A    
@@ -2628,20 +2628,37 @@
 Profile(
+BASIC_PROFILE_PATH
 );%0A
-%0A
         
@@ -3118,25 +3118,24 @@
 ference());%0A
-%0A
         asse
@@ -3199,33 +3199,32 @@
 .getVersion());%0A
-%0A
         assertEq
@@ -3292,25 +3292,24 @@
 niqueId());%0A
-%0A
         asse
@@ -3390,25 +3390,24 @@
 layName());%0A
-%0A
         asse
@@ -3593,21 +3593,16 @@
  get
-Basic
 Profile(
 ) %7B%0A
@@ -3589,32 +3589,43 @@
 file getProfile(
+String path
 ) %7B%0A        try%7B
@@ -3629,69 +3629,8 @@
 ry%7B%0A
-            String basicProfilePath = %22/basic_profile.ccpr%22;%0A
     
@@ -3686,37 +3686,25 @@
 rceAsStream(
-basicProfileP
+p
 ath);%0A      
@@ -3793,37 +3793,25 @@
 issing: %22 + 
-basicProfileP
+p
 ath);%0A      
@@ -4193,841 +4193,6 @@
  %7D%0A%0A
-    // Return a Profile object constructed from a profile.ccpr file explicitly containing the%0A    // fields necessary for multiple apps support%0A    private Profile getMultipleAppsProfile() %7B%0A        try%7B%0A            String basicProfilePath = %22/multiple_apps_profile.ccpr%22;%0A            InputStream is = this.getClass().getResourceAsStream(basicProfilePath);%0A            if(is == null) %7B%0A                throw new RuntimeException(%22Test resource missing: %22 + basicProfilePath);%0A            %7D%0A%0A            ProfileParser parser = new ProfileParser(is, mAppPlatform.getInstance(), mFreshResourceTable, %22profile%22,%0A                    Resource.RESOURCE_VERSION_UNKNOWN, false);%0A%0A            return parser.parse();%0A        %7D catch(Exception e) %7B%0A            throw PersistableSandbox.wrap(%22Error during profile test setup%22, e);%0A        %7D%0A    %7D%0A
 %7D%0A

unify getProfile method in tests