package com.drew.metadata.apple;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.makernotes.AppleMakernoteDirectory;
import com.drew.metadata.exif.makernotes.AppleRunTimeMakernoteDirectory;
import com.drew.metadata.plist.BplistReader;

/**
 * Reads the <tt>AppleRunTime</tt> data and adds {@link AppleRunTimeMakernoteDirectory} to the
 * parent {@link AppleMakernoteDirectory} if it can be parsed with no errors.
 */
public class AppleRunTimeReader {
    public void extract(@NotNull byte[] bytes, @NotNull final Metadata metadata, @NotNull final Directory parentDirectory) {
        parentDirectory.setByteArray(AppleMakernoteDirectory.TAG_RUN_TIME, bytes);

        if (!BplistReader.isValid(bytes)) {
            parentDirectory.addError("Input array is not a bplist");
            return;
        }

        AppleRunTimeMakernoteDirectory directory = new AppleRunTimeMakernoteDirectory();
        directory.setParent(parentDirectory);

        try {
            processAppleRunTime(directory, bytes);

            if (directory.getTagCount() > 0) {
                metadata.addDirectory(directory);
            }
        } catch (IOException e) {
            parentDirectory.addError("Error processing TAG_RUN_TIME: " + e.getMessage());
        }
    }

    /**
     * Process the BPLIST containing the RUN_TIME tag. The directory will only be populated with values
     * if the <tt>flag</tt> indicates that the CMTime structure is &quot;valid&quot;.
     *
     * @param directory The <tt>AppleRunTimeMakernoteDirectory</tt> to set values onto.
     * @param bplist    The BPLIST
     * @throws IOException Thrown if an error occurs parsing the BPLIST as a CMTime structure.
     */
    private static void processAppleRunTime(@NotNull final AppleRunTimeMakernoteDirectory directory, @NotNull final byte[] bplist) throws IOException {
        final BplistReader.PropertyListResults results = BplistReader.parse(bplist);

        final Set<Map.Entry<Byte, Byte>> entrySet = results.getEntrySet();

        if (entrySet != null) {
            HashMap<String, Object> values = new HashMap<String, Object>(entrySet.size());

            for (Map.Entry<Byte, Byte> entry : entrySet) {
                String key = (String) results.getObjects().get(entry.getKey());
                Object value = results.getObjects().get(entry.getValue());

                values.put(key, value);
            }

            // https://developer.apple.com/documentation/coremedia/cmtime-u58

            Object flagsObject = values.get("flags");
            if (flagsObject instanceof Byte) {
                byte flags = (Byte) flagsObject;
                if ((flags & 0x1) == 0x1) {
                    directory.setInt(AppleRunTimeMakernoteDirectory.CMTimeFlags, flags);
                    directory.setInt(AppleRunTimeMakernoteDirectory.CMTimeEpoch, (Byte) values.get("epoch"));
                    directory.setLong(AppleRunTimeMakernoteDirectory.CMTimeScale, (Long) values.get("timescale"));
                    directory.setLong(AppleRunTimeMakernoteDirectory.CMTimeValue, (Long) values.get("value"));
                }
            } else if (flagsObject instanceof String) {
                byte flags = Byte.parseByte((String) flagsObject);
                if ((flags & 0x1) == 0x1) {
                    directory.setInt(AppleRunTimeMakernoteDirectory.CMTimeFlags, flags);
                    directory.setInt(AppleRunTimeMakernoteDirectory.CMTimeEpoch, Byte.parseByte((String) values.get("epoch")));
                    directory.setLong(AppleRunTimeMakernoteDirectory.CMTimeScale, Long.parseLong((String) values.get("timescale")));
                    directory.setLong(AppleRunTimeMakernoteDirectory.CMTimeValue, Long.parseLong((String) values.get("value")));
                }
            }
        }
    }
}

@@ -636,17 +636,17 @@
 meReader
- 
+%0A
 %7B%0A    pu
@@ -761,16 +761,20 @@
 rectory)
+%0A   
  %7B%0A     
@@ -896,36 +896,33 @@
 tes)) %7B%0A        
-    
+%09
 parentDirectory.
@@ -970,20 +970,17 @@
         
-    
+%09
 return;%0A
@@ -1758,19 +1758,16 @@
  bplist 
-   
 The BPLI
@@ -2021,16 +2021,20 @@
 xception
+%0A   
  %7B%0A     
@@ -2410,17 +2410,16 @@
 (String)
- 
 results.
@@ -2678,760 +2678,49 @@
     
-Object flagsObject = values.get(%22flags%22);%0A            if (flagsObject instanceof Byte) %7B%0A                byte flags = (Byte) flagsObject;%0A                if ((flags & 0x1) == 0x1) %7B%0A                    directory.setInt(AppleRunTimeMakernoteDirectory.CMTimeFlags, flags);%0A                    directory.setInt(AppleRunTimeMakernoteDirectory.CMTimeEpoch, (Byte) values.get(%22epoch%22));%0A                    directory.setLong(AppleRunTimeMakernoteDirectory.CMTimeScale, (Long) values.get(%22timescale%22));%0A                    directory.setLong(AppleRunTimeMakernoteDirectory.CMTimeValue, (Long) values.get(%22value%22));%0A                %7D%0A            %7D else if (flagsObject instanceof String) %7B%0A                byte flags = Byte.parseByte((String) flagsObject);%0A    
+byte flags = (Byte)values.get(%22flags%22);%0A%0A
     
@@ -2751,28 +2751,24 @@
 ) == 0x1) %7B%0A
-    
             
@@ -2848,36 +2848,32 @@
                 
-    
 directory.setInt
@@ -2921,32 +2921,14 @@
 ch, 
-Byte.parseByte((String) 
+(Byte)
 valu
@@ -2943,23 +2943,18 @@
 epoch%22))
-)
 ;%0A
-    
         
@@ -3027,32 +3027,14 @@
 le, 
-Long.parseLong((String) 
+(Long)
 valu
@@ -3049,31 +3049,26 @@
 timescale%22))
-)
 ;%0A
-    
             
@@ -3137,72 +3137,35 @@
 ue, 
-Long.parseLong((String) values.get(%22value%22)));%0A                %7D
+(Long)values.get(%22value%22));
 %0A   

Revert "Sometimes the Apple metadata is stored as a String. This fix will prevent a ClassCastException"