// (c) J. Dean Brock, Rebecca F. Bruce, 2003, 2004, 2006, 2008
// Department of Computer Science
// University of North Carolina at Asheville

// Derived from UNCA CSCI 201 Plot-2D lab
// (c) J. Dean Brock, 1998


// If you are solving this lab, you should ignore everything
// above the comment that says:
// ***   START YOUR WORK HERE   ***
// Well, we really mean the next comment that says that.


public class ArrayLab {


  // The reflection stuff Joe likes so much
  private static java.util.Vector<java.lang.reflect.Method> operators ;
  private static java.util.Vector<java.lang.reflect.Method> modifiers ;
  
  private static void setMethodVectors() {
    // The reflection stuff that Joe likes so much
    Class<ArrayLab> classInfo = ArrayLab.class ;
 
    operators = new java.util.Vector<java.lang.reflect.Method>() ;
    modifiers = new java.util.Vector<java.lang.reflect.Method>() ;
    
    try {
      java.lang.reflect.Method[] methods = classInfo.getDeclaredMethods() ;
      for (java.lang.reflect.Method m : methods) {
        Class[] params = m.getParameterTypes() ;
        if (params.length == 1
              && params[0].isArray()
              && params[0].getComponentType().equals(int.class)) {
          Class retType = m.getReturnType() ;
          if (retType.equals(int.class)) {
            operators.add(m) ;
          } else if (retType.equals(void.class)) {
            modifiers.add(m) ;
          }
        }
      }
    } catch (SecurityException se) { }
  }

  private static void testOperatorMethods(Sound s) {
    if (operators != null) {
      ArrayLab obj = new ArrayLab() ;
      for (int i=0; i<operators.size(); ++i) {
        java.lang.reflect.Method m = operators.elementAt(i) ;
        try {
          int[] samples = getValues(s) ;
          int result = (Integer) m.invoke(obj, samples) ;
          System.out.println("Operator " + m.getName() + " returns " + result) ;
        } catch (Exception e) {
          System.out.println("Operator " + m.getName() + " --> " + e) ;
        }
      }
    }
  }
  
  private static void testModifierMethods(Sound s) {
    if (modifiers != null) {
      ArrayLab obj = new ArrayLab() ;
      for (int i=0; i<modifiers.size(); ++i) {
        java.lang.reflect.Method m = modifiers.elementAt(i) ;
        try {
          int[] samples = getValues(s) ;
          m.invoke(obj, samples) ;
          Sound newS = new Sound(s) ;
          for (int p=0; p<newS.getNumSamples(); ++p) {
            newS.setSampleValueAt(p, samples[p]) ;
          }
          exploreTitled(newS, m.getName()) ;
        } catch (Exception e) {
          System.out.println("Operator " + m.getName() + " --> " + e) ;
        }
      }
    }
  }
  
  private static void exploreTitled(Sound s, String t) {
    SoundExplorer e = new SoundExplorer(new Sound(s), false) ;
    e.setTitle(t) ;
  }

  private static int[] getValues(Sound s) {
    int numSamples = s.getNumSamples() ;
    int[] retV = new int[numSamples] ;
    for (int i=0; i<numSamples; ++i) {
      retV[i] = s.getSampleValueAt(i) ;
    }
    return retV ;
  }
  
  public static void main(String[] args) {
    
    setMethodVectors() ;
    
    Sound sndBraes = new Sound(fileBraes) ;
    exploreTitled(sndBraes, "original") ;
    
    testOperatorMethods(sndBraes) ; 
    testModifierMethods(sndBraes) ;
  }

  // ***   START YOUR WORK HERE   ***

  // You will need to change the next line if you are not using the common file
  private static final String fileBraes = "/home/brock/public_html/classes/Fall2008/201labs/images/braes.wav" ;

  // Write a method that returns the sum of the array elements
  private static int getSum(int[] v) {
    int sum = 0 ;
    for (int x : v) {
      sum = sum + x ;
    }
    return sum ;
  }
  
  // Write a method that returns the largest element of the array
  private static int getMostPositive(int[] v) {
    return 201 ;
  }
  
  // Write a method that returns the smallest element of the array
  private static int getMostNegative(int[] v) {
    return 201 ;
  }

  // Write a method that returns the average of the array elements
  // HINT:  Use getSum
  private static int getAverage(int[] v) {
    return 201 ;
  }

  // Write a method that returns the sum of the most postive
  // and most negative elements of the array
  // HINT:  Use getMostPositive and getMostNegative
  private static int getExtremes(int[] v) {
    return 201 ;
  }
  
  // Write a method that divides every element of the array by three
  private static void divideBy3(int[] v) {
  }

  // Write a method that repeats the first half of the array twice
  private static void repeatFirstHalf(int[] v) {
  }

  // Write a method that "plays" the song twice,
  // each time twice as fast.
  private static void doubleTime(int[] v) {
  }

  // Write a method that plays the song four times as fast for four times
  // HINT:  Sounds like double doubleTime to me
  private static void quadrupleTime(int[] v) {
  }

  // Write a method that reverses the song
  private static void reverse(int[] v) {
  }
  
  // Write a method that generates A440
  private static int samplingRate = 44100 ; 
  private static int frequency    = 440 ;
  private static int amplitude    = 20000 ;
  private static void setA440(int[] v) {
    for (int i=0; i<v.length; ++i) {
      double realSlotTime = (i + 0.5)/samplingRate ;
      double angleRadians = 2 * Math.PI * frequency * realSlotTime  ;
      double sineValue    = Math.sin(angleRadians) ;
      int    sampleValue  = (int)Math.round(sineValue*amplitude) ;
      v[i] = sampleValue ;
    }
  }
}

