/* Copyright 2004-2005 the original author or authors.
 * 
 * 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.
 */
package org.codehaus.groovy.grails.web.servlet;

import groovy.lang.GroovySystem;
import groovy.lang.MetaClass;
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * Grails implementation of Flash scope (@see org.codehaus.groovy.grails.web.servlet.FlashScope)
 *
 * @author Graeme Rocher
 * @since 07-Feb-2006
 */
public class GrailsFlashScope implements FlashScope {
    private HashMap current = new HashMap();
    private HashMap next = new HashMap();
    private static final String ERRORS_PREFIX = "org.codehaus.groovy.grails.ERRORS_";
    private static final String ERRORS_PROPERTY = "errors";

    public GrailsFlashScope() {
    }

    public void next() {
        current.clear();
        current = (HashMap)next.clone();
        next.clear();
        reassociateObjectsWithErrors(current);
    }

    private void reassociateObjectsWithErrors(Map scope) {
        for (Iterator i = scope.keySet().iterator(); i.hasNext();) {
            Object key =  i.next();
            Object value = scope.get(key);
            if(value instanceof Map) {
                reassociateObjectsWithErrors((Map)value);
            }
            String errorsKey = ERRORS_PREFIX + System.identityHashCode(value);
            Object errors = scope.get(errorsKey);
            if(value!=null && errors != null) {
                MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(value.getClass());
                if(mc.hasProperty(value, ERRORS_PROPERTY)!=null) {
                    mc.setProperty(value, ERRORS_PROPERTY, errors);
                }
            }

        }
    }

    public int size() {
        return current.size() + next.size();
    }

    public void clear() {
        current.clear();
        next.clear();
    }

    public boolean isEmpty() {
        return size() == 0;
    }

    public boolean containsKey(Object key) {
        return (current.containsKey(key) || next.containsKey(key));
    }

    public boolean containsValue(Object value) {
        return (current.containsValue(value) || next.containsValue(value));
    }

    public Collection values() {
        Collection c = new ArrayList();
        c.addAll(current.values());
        c.addAll(next.values());
        return c;
    }

    public void putAll(Map t) {
        for (Iterator i = t.keySet().iterator(); i.hasNext();) {
            Object key = i.next();
            put(key,t.get(key));
        }
    }

    public Set entrySet() {
        Set keySet = new HashSet();
        keySet.addAll(current.entrySet());
        keySet.addAll(next.entrySet());
        return keySet;
    }

    public Set keySet() {
        Set keySet = new HashSet();
        keySet.addAll(current.keySet());
        keySet.addAll(next.keySet());
        return keySet;
    }

    public Object get(Object key) {
        if(next.containsKey(key))
            return next.get(key);
        return current.get(key);
    }

    public Object remove(Object key) {
        if(current.containsKey(key))
            return current.remove(key);
        else
            return next.remove(key);
    }

    public Object put(Object key, Object value) {
        // create the session if it doesn't exist
        registerWithSessionIfNecessary();
        if(current.containsKey(key)) {
            current.remove(key);
        }
        storeErrorsIfPossible(value);

        return next.put(key,value);
    }

    private void storeErrorsIfPossible(Object value) {
        if(value != null) {

            MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(value.getClass());
            if(mc.hasProperty(value, ERRORS_PROPERTY)!=null) {
                Object errors = mc.getProperty(value, ERRORS_PROPERTY);
                if(errors != null) {
                    next.put(ERRORS_PREFIX + System.identityHashCode(value), errors);
                }
            }
        }
    }

    private void registerWithSessionIfNecessary() {
        GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes();
        HttpSession session = webRequest.getCurrentRequest().getSession(true);
        if(session.getAttribute(GrailsApplicationAttributes.FLASH_SCOPE) == null) session.setAttribute(GrailsApplicationAttributes.FLASH_SCOPE, this);
    }
}

@@ -1883,32 +1883,482 @@
 ;%0A            %7D%0A
+            reasssociateObjectWithErrors(scope, value);%0A%0A        %7D%0A    %7D%0A%0A    private void reasssociateObjectWithErrors(Map scope, Object value) %7B%0A        if(value instanceof Collection) %7B%0A             Collection values = (Collection)value;%0A            for (Iterator i = values.iterator(); i.hasNext();) %7B%0A                Object current = i.next();%0A                reasssociateObjectWithErrors(scope, current);%0A            %7D%0A        %7D%0A        else %7B%0A
             Stri
@@ -2412,32 +2412,32 @@
 ashCode(value);%0A
-
             Obje
@@ -2762,32 +2762,32 @@
               %7D%0A
+
             %7D%0A%0A 
@@ -2780,25 +2780,24 @@
           %7D%0A
-%0A
         %7D%0A  
@@ -4679,16 +4679,352 @@
 ull) %7B%0A%0A
+            if(value instanceof Collection) %7B%0A                Collection values = (Collection)value;%0A                for (Iterator i = values.iterator(); i.hasNext();) %7B%0A                    Object current = i.next();%0A                    storeErrorsIfPossible(current);%0A                %7D            %0A            %7D%0A            else %7B%0A    
         
@@ -5102,32 +5102,36 @@
 ue.getClass());%0A
+    
             if(m
@@ -5185,32 +5185,36 @@
                 
+    
 Object errors = 
@@ -5269,16 +5269,20 @@
         
+    
 if(error
@@ -5310,24 +5310,28 @@
             
+   
+ 
 next.put(ERR
@@ -5376,32 +5376,54 @@
 alue), errors);%0A
+                    %7D%0A
                 
@@ -5416,32 +5416,32 @@
               %7D%0A
-
             %7D%0A  
@@ -5430,32 +5430,33 @@
 %7D%0A            %7D%0A
+%0A
         %7D%0A    %7D%0A

fix for GRAILS-2452