// This code grabbed (then modified) from:
//
// http://getablogger.blogspot.com/2008/01/android-how-to-post-file-to-php-server.html
//
// The idea of using this class is to run a simple PHP file uploader on the
// server and upload images via http (which lets me use existing technology
// rather than building a custom server to run on the host).

package my.lan.zooty.upwithphp;

import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.InputStream;
import android.net.Uri;
import android.database.Cursor;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.content.ContentResolver;
import android.text.format.Time;
import android.widget.Toast;
import android.util.Log;

public class UploadFileToPhp implements Runnable {

   URL connectURL;
   String responseString;
   Uri contentUri;
   String Tag="UploadFileToPhp";
   InputStream ins = null;
   String fileName;
   boolean downcase;
   UpWithPhpActivity myAct;
   String myname;
   ContentResolver cr;
   long knownFileSize = 0;
   byte[] contentBytes = null;
   String genFileSuffix = "dat";
   boolean initOK = false;

   // I get a byte[] buffer from the camera callback when I take a picture,
   // so just pass that buffer to the uploader along with the name I want to
   // give the file when it is uploaded along with the URL for the web
   // server where I'll upload it.

   UploadFileToPhp(UpWithPhpActivity myAct,
                   String urlString,
                   boolean downcase,
                   Uri contentUri,
                   String contentText) {
      Log.i(Tag,"In UploadFileToPhp constructor, uri: " + contentUri);
      this.myAct = myAct;
      ContentResolver cr = this.myAct.getContentResolver();
      try {
         connectURL = new URL(urlString);
      } catch(Exception ex){
         String msg =
            myAct.getResources().getString(R.string.msg_bad_upload_url) +
            "\"" + urlString + "\"";
         Toast.makeText(myAct,msg,Toast.LENGTH_LONG).show();
         return;
      }
      this.contentUri = contentUri;
      this.downcase = downcase;

      if (this.contentUri != null) {
         fileName = parseUriToFilename(this.contentUri);
         if (fileName != null) {
            Log.i(Tag,"file name found: " + fileName);
            File myfile = new File(fileName);
            if (myfile.exists()) {
               knownFileSize = myfile.length();
               Log.i(Tag,"file size is " + knownFileSize);
            } else {
               String msg =
                  myAct.getResources().getString(R.string.msg_no_file) +
                  "\"" + fileName + "\"";
               Toast.makeText(myAct,msg,Toast.LENGTH_LONG).show();
               fileName = null;
            }
         }
         if (fileName == null) {
            try {
               Log.i(Tag,"No file, trying open via content resolver");
               ins = cr.openInputStream(this.contentUri);
               myname = this.contentUri.getLastPathSegment();
            } catch (FileNotFoundException e) {
               String msg =
                  myAct.getResources().getString(R.string.msg_no_open) +
                  "\"" + this.contentUri + "\"";
               Toast.makeText(myAct,msg,Toast.LENGTH_LONG).show();
               ins = null;
               e.printStackTrace();
            }
         } else {
            File myfile = new File(fileName);
            myname = myfile.getName();
            if (downcase) {
               myname = myname.toLowerCase();
            }
            try {
               ins = new FileInputStream(fileName);
            } catch (FileNotFoundException e) {
               String msg =
                  myAct.getResources().getString(R.string.msg_no_file) +
                  "\"" + fileName + "\"";
               Toast.makeText(myAct,msg,Toast.LENGTH_LONG).show();
               ins = null;
               e.printStackTrace();
            }
         }
      }
      if ((ins == null) && (contentText != null)) {
         contentBytes = contentText.getBytes();
         knownFileSize = contentBytes.length;
         genFileSuffix = "txt";
      }
      if (myname == null) {
         Log.i(Tag,"Generating filename");
         Time now = new Time();
         now.setToNow();
         myname = now.format("UpWithPhp-%Y-%m-%d-%H-%M-%S.") + genFileSuffix;
      }
      initOK = ((ins != null) || (contentBytes != null));
   }

   // Open the web server and upload the file in the separate thread.

   public void run() {
      Log.i(Tag, "In UploadFileToPhp run()");
      String lineEnd = "\r\n";
      String twoHyphens = "--";
      String boundary = "*****14cac5ad-6280-4a5c-b012-bdfcc73883ac*****";
      if (initOK) try {

         //------------------ CLIENT REQUEST

         Log.e(Tag,"Starting to upload file");

         // Open a HTTP connection to the URL

         HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();

         // Allow Inputs
         conn.setDoInput(true);

         // Allow Outputs
         conn.setDoOutput(true);

         // Don't use a cached copy.
         conn.setUseCaches(false);

         // Use a post method.
         conn.setRequestMethod("POST");

         conn.setRequestProperty("Connection", "Keep-Alive");

         conn.setRequestProperty("Content-Type",
            "multipart/form-data;boundary="+boundary);

         // Won't send big files properly without this (use default
         // chunk size).
         conn.setChunkedStreamingMode(0);

         DataOutputStream dos = new DataOutputStream( conn.getOutputStream() );

         dos.writeBytes(twoHyphens + boundary + lineEnd);

         // This is hard coded for data for the example uploader.php
         // script I document in the manual. Perhaps I should allow
         // the form data to be customized as well as the uploader
         // url...

         dos.writeBytes("Content-Disposition: " +
            "form-data; name=\"userfile\";filename=\"" + myname +"\"" + lineEnd);

         // Call everything application/octet-stream because I just want to
         // make sure the bytes get uploaded intact, and calling it binary
         // data is the best way to do that.

         dos.writeBytes("Content-Type: application/octet-stream" + lineEnd);
         dos.writeBytes(lineEnd);

         Log.e(Tag,"Headers are written");

         myAct.runOnUiThread(new Runnable() {
                                public void run() {
                                   myAct.doUploadStart(knownFileSize);
                                }
                             });

         Log.e(Tag,"Progress dialog started.");

         if (ins != null) {
            // Write the file. This would be a good loop to add callbacks
            // for progress bar updates.

            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            while (ins.available() > 0) {
               int wanted = bufferSize;
               if (ins.available() < wanted) {
                  wanted = ins.available();
               }
               wanted = ins.read(buffer, 0, wanted);
               dos.write(buffer, 0, wanted);
            }
         } else if (contentBytes != null) {
            // Just write the text string to the file.
            dos.write(contentBytes, 0, contentBytes.length);
         }

         // send multipart form data necesssary after file data...

         dos.writeBytes(lineEnd);
         dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

         // close streams

         Log.e(Tag,"File is written");
         dos.flush();

         InputStream is = conn.getInputStream();

         // retrieve the response from server

         int ch;

         StringBuffer b =new StringBuffer();
         while( ( ch = is.read() ) != -1 ){
            b.append( (char)ch );
         }
         String s=b.toString(); 
         Log.i("Response",s);
         dos.close();
      } catch (MalformedURLException ex) {
         String msg =
            myAct.getResources().getString(R.string.msg_bad_upload_url) +
            "\"" + ex.getMessage() + "\"";
         Toast.makeText(myAct,msg,Toast.LENGTH_LONG).show();
         Log.e(Tag, "error: " + ex.getMessage(), ex);
      } catch (IOException ioe) {
         String msg =
            myAct.getResources().getString(R.string.msg_io_err) +
            "\"" + ioe.getMessage() + "\"";
         Toast.makeText(myAct,msg,Toast.LENGTH_LONG).show();
         Log.e(Tag, "error: " + ioe.getMessage(), ioe);
      }

      // Do a RunOnUiThread call to tell UI the picture is uploaded and it
      // can go ahead and finish.

      myAct.runOnUiThread(new Runnable() {
                             public void run() {
                                myAct.doUploadComplete();
                             }
                          });
   }

   public String parseUriToFilename(Uri uri) {

      // Special case file and media Uri's which I know how to get an actual
      // filename for. Return null if techniques in here are unable to dig
      // up filename.

      String selectedImagePath = null;
      String filemanagerPath = uri.getPath();

      String[] projection = { MediaStore.Images.Media.DATA };
      Cursor cursor = myAct.managedQuery(uri, projection, null, null, null);

      if (cursor != null) {

         // Here you will get a null pointer if cursor is null
         // This can be if you used OI file manager for picking the media

         int column_index = cursor.getColumnIndexOrThrow(
                                      MediaStore.Images.Media.DATA);
         cursor.moveToFirst();
         selectedImagePath = cursor.getString(column_index);
      }

      if (selectedImagePath != null) {
         return selectedImagePath;
      } else if (filemanagerPath != null) {
         return filemanagerPath;
      }
      return null;
   }
}
