package jp.juggler.subwaytooter.util;

import android.annotation.SuppressLint;
import android.content.Context;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

import android.content.res.Resources;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.storage.StorageManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Base64;
import android.util.SparseBooleanArray;

import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.webkit.MimeTypeMap;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONObject;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import java.text.DecimalFormat;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import jp.juggler.subwaytooter.ActMain;

public class Utils {
	
	@SuppressLint("DefaultLocale")
	public static String formatTimeDuration( long t ){
		StringBuilder sb = new StringBuilder();
		long n;
		// day
		n = t / 86400000L;
		if( n > 0 ){
			sb.append( String.format( Locale.JAPAN, "%dd", n ) );
			t -= n * 86400000L;
		}
		// h
		n = t / 3600000L;
		if( n > 0 || sb.length() > 0 ){
			sb.append( String.format( Locale.JAPAN, "%dh", n ) );
			t -= n * 3600000L;
		}
		// m
		n = t / 60000L;
		if( n > 0 || sb.length() > 0 ){
			sb.append( String.format( Locale.JAPAN, "%dm", n ) );
			t -= n * 60000L;
		}
		// s
		float f = t / 1000f;
		sb.append( String.format( Locale.JAPAN, "%.03fs", f ) );
		
		return sb.toString();
	}
	
	static DecimalFormat bytes_format = new DecimalFormat( "#,###" );
	
	public static String formatBytes( long t ){
		return bytes_format.format( t );

//		StringBuilder sb = new StringBuilder();
//		long n;
//		// giga
//		n = t / 1000000000L;
//		if( n > 0 ){
//			sb.append( String.format( Locale.JAPAN, "%dg", n ) );
//			t -= n * 1000000000L;
//		}
//		// Mega
//		n = t / 1000000L;
//		if( sb.length() > 0 ){
//			sb.append( String.format( Locale.JAPAN, "%03dm", n ) );
//			t -= n * 1000000L;
//		}else if( n > 0 ){
//			sb.append( String.format( Locale.JAPAN, "%dm", n ) );
//			t -= n * 1000000L;
//		}
//		// kilo
//		n = t / 1000L;
//		if( sb.length() > 0 ){
//			sb.append( String.format( Locale.JAPAN, "%03dk", n ) );
//			t -= n * 1000L;
//		}else if( n > 0 ){
//			sb.append( String.format( Locale.JAPAN, "%dk", n ) );
//			t -= n * 1000L;
//		}
//		// remain
//		if( sb.length() > 0 ){
//			sb.append( String.format( Locale.JAPAN, "%03d", t ) );
//		}else if( n > 0 ){
//			sb.append( String.format( Locale.JAPAN, "%d", t ) );
//		}
//
//		return sb.toString();
	}
	
	//	public static PendingIntent createAlarmPendingIntent( Context context ){
//		Intent i = new Intent( context.getApplicationContext(), Receiver1.class );
//		i.setAction( Receiver1.ACTION_ALARM );
//		return PendingIntent.getBroadcast( context.getApplicationContext(), 0, i, 0 );
//	}
//
	// 文字列とバイト列の変換
	public static byte[] encodeUTF8( String str ){
		try{
			return str.getBytes( "UTF-8" );
		}catch( Throwable ex ){
			return null; // 入力がnullの場合のみ発生
		}
	}
	
	// 文字列とバイト列の変換
	public static String decodeUTF8( byte[] data ){
		try{
			return new String( data, "UTF-8" );
		}catch( Throwable ex ){
			return null; // 入力がnullの場合のみ発生
		}
	}
	
	// 文字列と整数の変換
	public static int parse_int( String v, int defval ){
		try{
			return Integer.parseInt( v, 10 );
		}catch( Throwable ex ){
			return defval;
		}
	}
	
	public static String optStringX( JSONObject src, String key){
		return  src.isNull( key ) ? null : src.optString( key );
	}

	public static String optStringX( JSONArray src, int key){
		return  src.isNull( key ) ? null : src.optString( key );
	}
	
	public static ArrayList< String > parseStringArray( LogCategory log, JSONArray array ){
		ArrayList< String > dst_list = new ArrayList<>(  );
		if( array != null ){
			for(int i=0,ie=array.length();i<ie;++i){
				String sv = Utils.optStringX(array,i);
				dst_list.add( sv );
			}
		}
		return dst_list;
	}
	
	static final char[] hex = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	
	public static void addHex( StringBuilder sb, byte b ){
		sb.append( hex[ ( b >> 4 ) & 15 ] );
		sb.append( hex[ ( b ) & 15 ] );
	}
	
	public static int hex2int( int c ){
		switch( c ){
		default:
			return 0;
		case '0':
			return 0;
		case '1':
			return 1;
		case '2':
			return 2;
		case '3':
			return 3;
		case '4':
			return 4;
		case '5':
			return 5;
		case '6':
			return 6;
		case '7':
			return 7;
		case '8':
			return 8;
		case '9':
			return 9;
		case 'a':
			return 0xa;
		case 'b':
			return 0xb;
		case 'c':
			return 0xc;
		case 'd':
			return 0xd;
		case 'e':
			return 0xe;
		case 'f':
			return 0xf;
		case 'A':
			return 0xa;
		case 'B':
			return 0xb;
		case 'C':
			return 0xc;
		case 'D':
			return 0xd;
		case 'E':
			return 0xe;
		case 'F':
			return 0xf;
		}
	}
	
	// 16進ダンプ
	public static String encodeHex( byte[] data ){
		if( data == null ) return null;
		StringBuilder sb = new StringBuilder();
		for( byte b : data ){
			addHex( sb, b );
		}
		return sb.toString();
	}
	
	public static byte[] encodeSHA256( byte[] src ){
		try{
			MessageDigest digest = MessageDigest.getInstance( "SHA-256" );
			digest.reset();
			return digest.digest( src );
		}catch( NoSuchAlgorithmException e1 ){
			return null;
		}
	}
	
	public static String encodeBase64Safe( byte[] src ){
		try{
			return Base64.encodeToString( src, Base64.URL_SAFE );
		}catch( Throwable ex ){
			return null;
		}
	}
	
	public static String url2name( String url ){
		if( url == null ) return null;
		return encodeBase64Safe( encodeSHA256( encodeUTF8( url ) ) );
	}

//	public static String name2url(String entry) {
//		if(entry==null) return null;
//		byte[] b = new byte[entry.length()/2];
//		for(int i=0,ie=b.length;i<ie;++i){
//			b[i]= (byte)((hex2int(entry.charAt(i*2))<<4)| hex2int(entry.charAt(i*2+1)));
//		}
//		return decodeUTF8(b);
//	}
	
	///////////////////////////////////////////////////
	
	// MD5ハッシュの作成
	public static String digestMD5( String s ){
		if( s == null ) return null;
		try{
			MessageDigest md = MessageDigest.getInstance( "MD5" );
			md.reset();
			return encodeHex( md.digest( s.getBytes( "UTF-8" ) ) );
		}catch( Throwable ex ){
			ex.printStackTrace();
		}
		return null;
	}
	
	/////////////////////////////////////////////
	
	static HashMap< Character, String > taisaku_map = new HashMap<>();
	static SparseBooleanArray taisaku_map2 = new SparseBooleanArray();
	
	static void _taisaku_add_string( String z, String h ){
		for( int i = 0, e = z.length() ; i < e ; ++ i ){
			char zc = z.charAt( i );
			taisaku_map.put( zc, "" + Character.toString( h.charAt( i ) ) );
			taisaku_map2.put( (int) zc, true );
		}
	}
	
	static{
		taisaku_map = new HashMap<>();
		taisaku_map2 = new SparseBooleanArray();
		
		// tilde,wave dash,horizontal ellipsis,minus sign
		_taisaku_add_string(
			"\u2073\u301C\u22EF\uFF0D"
			, "\u007e\uFF5E\u2026\u2212"
		);
		// zenkaku to hankaku
		_taisaku_add_string(
			"　！”＃＄％＆’（）＊＋，－．／０１２３４５６７８９：；＜＝＞？＠ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ［］＾＿｀ａｂｃｄｅｆｇｈｉｊｋｌｍｎｏｐｑｒｓｔｕｖｗｘｙｚ｛｜｝"
			, " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}"
		);
		
	}
	
	static boolean isBadChar2( char c ){
		return c == 0xa || taisaku_map2.get( (int) c );
	}
	
	//! フォントによって全角文字が化けるので、その対策
	public static String font_taisaku( String text, boolean lf2br ){
		if( text == null ) return null;
		int l = text.length();
		StringBuilder sb = new StringBuilder( l );
		if( ! lf2br ){
			for( int i = 0 ; i < l ; ++ i ){
				int start = i;
				while( i < l && ! taisaku_map2.get( (int) text.charAt( i ) ) ) ++ i;
				if( i > start ){
					sb.append( text.substring( start, i ) );
					if( i >= l ) break;
				}
				sb.append( taisaku_map.get( text.charAt( i ) ) );
			}
		}else{
			for( int i = 0 ; i < l ; ++ i ){
				int start = i;
				while( i < l && ! isBadChar2( text.charAt( i ) ) ) ++ i;
				if( i > start ){
					sb.append( text.substring( start, i ) );
					if( i >= l ) break;
				}
				char c = text.charAt( i );
				if( c == 0xa ){
					sb.append( "<br/>" );
				}else{
					sb.append( taisaku_map.get( c ) );
				}
			}
		}
		return sb.toString();
	}
	
	////////////////////////////
	
	public static String toLower( String from ){
		if( from == null ) return null;
		return from.toLowerCase( Locale.US );
	}
	
	public static String toUpper( String from ){
		if( from == null ) return null;
		return from.toUpperCase( Locale.US );
	}
	
	public static String getString( Bundle b, String key, String defval ){
		try{
			String v = b.getString( key );
			if( v != null ) return v;
		}catch( Throwable ignored ){
		}
		return defval;
	}
	
	public static byte[] loadFile( File file ) throws IOException{
		int size = (int) file.length();
		byte[] data = new byte[ size ];
		FileInputStream in = new FileInputStream( file );
		try{
			int nRead = 0;
			while( nRead < size ){
				int delta = in.read( data, nRead, size - nRead );
				if( delta <= 0 ) break;
			}
			return data;
		}finally{
			try{
				in.close();
			}catch( Throwable ignored ){
			}
		}
	}
	
	public static String ellipsize( String t, int max ){
		return ( t.length() > max ? t.substring( 0, max - 1 ) + "…" : t );
	}

//	public static int getEnumStringId( String residPrefix, String name,Context context ) {
//		name = residPrefix + name;
//		try{
//			int iv = context.getResources().getIdentifier(name,"string",context.getPackageName() );
//			if( iv != 0 ) return iv;
//		}catch(Throwable ex){
//		}
//		log.e("missing resid for %s",name);
//		return R.string.Dialog_Cancel;
//	}

//	public static String getConnectionResultErrorMessage( ConnectionResult connectionResult ){
//		int code = connectionResult.getErrorCode();
//		String msg = connectionResult.getErrorMessage();
//		if( TextUtils.isEmpty( msg ) ){
//			switch( code ){
//			case ConnectionResult.SUCCESS:
//				msg = "SUCCESS";
//				break;
//			case ConnectionResult.SERVICE_MISSING:
//				msg = "SERVICE_MISSING";
//				break;
//			case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
//				msg = "SERVICE_VERSION_UPDATE_REQUIRED";
//				break;
//			case ConnectionResult.SERVICE_DISABLED:
//				msg = "SERVICE_DISABLED";
//				break;
//			case ConnectionResult.SIGN_IN_REQUIRED:
//				msg = "SIGN_IN_REQUIRED";
//				break;
//			case ConnectionResult.INVALID_ACCOUNT:
//				msg = "INVALID_ACCOUNT";
//				break;
//			case ConnectionResult.RESOLUTION_REQUIRED:
//				msg = "RESOLUTION_REQUIRED";
//				break;
//			case ConnectionResult.NETWORK_ERROR:
//				msg = "NETWORK_ERROR";
//				break;
//			case ConnectionResult.INTERNAL_ERROR:
//				msg = "INTERNAL_ERROR";
//				break;
//			case ConnectionResult.SERVICE_INVALID:
//				msg = "SERVICE_INVALID";
//				break;
//			case ConnectionResult.DEVELOPER_ERROR:
//				msg = "DEVELOPER_ERROR";
//				break;
//			case ConnectionResult.LICENSE_CHECK_FAILED:
//				msg = "LICENSE_CHECK_FAILED";
//				break;
//			case ConnectionResult.CANCELED:
//				msg = "CANCELED";
//				break;
//			case ConnectionResult.TIMEOUT:
//				msg = "TIMEOUT";
//				break;
//			case ConnectionResult.INTERRUPTED:
//				msg = "INTERRUPTED";
//				break;
//			case ConnectionResult.API_UNAVAILABLE:
//				msg = "API_UNAVAILABLE";
//				break;
//			case ConnectionResult.SIGN_IN_FAILED:
//				msg = "SIGN_IN_FAILED";
//				break;
//			case ConnectionResult.SERVICE_UPDATING:
//				msg = "SERVICE_UPDATING";
//				break;
//			case ConnectionResult.SERVICE_MISSING_PERMISSION:
//				msg = "SERVICE_MISSING_PERMISSION";
//				break;
//			case ConnectionResult.RESTRICTED_PROFILE:
//				msg = "RESTRICTED_PROFILE";
//				break;
//
//			}
//		}
//		return msg;
//	}

//	public static String getConnectionSuspendedMessage( int i ){
//		switch( i ){
//		default:
//			return "?";
//		case GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST:
//			return "NETWORK_LOST";
//		case GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED:
//			return "SERVICE_DISCONNECTED";
//		}
//	}
	
	static HashMap< String, String > mime_type_ex = null;
	static final Object mime_type_ex_lock = new Object();
	
	static String findMimeTypeEx( String ext ){
		synchronized( mime_type_ex_lock ){
			if( mime_type_ex == null ){
				HashMap< String, String > tmp = new HashMap<>();
				tmp.put( "BDM", "application/vnd.syncml.dm+wbxml" );
				tmp.put( "DAT", "" );
				tmp.put( "TID", "" );
				tmp.put( "js", "text/javascript" );
				tmp.put( "sh", "application/x-sh" );
				tmp.put( "lua", "text/x-lua" );
				mime_type_ex = tmp;
			}
			return mime_type_ex.get( ext );
		}
	}
	
	public static final String MIME_TYPE_APPLICATION_OCTET_STREAM = "application/octet-stream";
	
	public static String getMimeType( LogCategory log, String src ){
		String ext = MimeTypeMap.getFileExtensionFromUrl( src );
		if( ! TextUtils.isEmpty( ext ) ){
			ext = ext.toLowerCase( Locale.US );
			
			//
			String mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension( ext );
			if( ! TextUtils.isEmpty( mime_type ) ) return mime_type;
			
			//
			mime_type = findMimeTypeEx( ext );
			if( ! TextUtils.isEmpty( mime_type ) ) return mime_type;
			
			// 戻り値が空文字列の場合とnullの場合があり、空文字列の場合は既知でありログ出力しない
			
			if( mime_type == null && log != null )
				log.w( "getMimeType(): unknown file extension '%s'", ext );
		}
		return MIME_TYPE_APPLICATION_OCTET_STREAM;
	}
	
	public static CharSequence formatSpannable1( Context context, int string_id, CharSequence display_name ){
		String s = context.getString( string_id );
		int end = s.length();
		int pos = s.indexOf( "%1$s" );
		if( pos == -1 ) return s;
		SpannableStringBuilder sb = new SpannableStringBuilder(  );
		if( pos > 0 ) sb.append(s.substring( 0,pos ));
		sb.append( display_name);
		if( pos +4 < end ) sb.append(s.substring( pos+4,end ));
		return sb;
	}
	
	static class FileInfo {
		
		Uri uri;
		String mime_type;
		
		FileInfo( String any_uri ){
			if( any_uri == null ) return;
			
			if( any_uri.startsWith( "/" ) ){
				uri = Uri.fromFile( new File( any_uri ) );
			}else{
				uri = Uri.parse( any_uri );
			}
			
			String ext = MimeTypeMap.getFileExtensionFromUrl( any_uri );
			if( ext != null ){
				mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension( ext.toLowerCase() );
			}
		}
	}
	
	static
	@NonNull
	Map< String, String > getSecondaryStorageVolumesMap( Context context ){
		Map< String, String > result = new HashMap<>();
		try{
			
			StorageManager sm = (StorageManager) context.getApplicationContext().getSystemService( Context.STORAGE_SERVICE );
			
			// SDカードスロットのある7.0端末が手元にないから検証できない
//			if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ){
//				for(StorageVolume volume : sm.getStorageVolumes() ){
//					// String path = volume.getPath();
//					String state = volume.getState();
//
//				}
//			}
			
			Method getVolumeList = sm.getClass().getMethod( "getVolumeList" );
			Object[] volumes = (Object[]) getVolumeList.invoke( sm );
			//
			for( Object volume : volumes ){
				Class< ? > volume_clazz = volume.getClass();
				
				String path = (String) volume_clazz.getMethod( "getPath" ).invoke( volume );
				String state = (String) volume_clazz.getMethod( "getState" ).invoke( volume );
				if( ! TextUtils.isEmpty( path ) && "mounted".equals( state ) ){
					//
					boolean isPrimary = (Boolean) volume_clazz.getMethod( "isPrimary" ).invoke( volume );
					if( isPrimary ) result.put( "primary", path );
					//
					String uuid = (String) volume_clazz.getMethod( "getUuid" ).invoke( volume );
					if( ! TextUtils.isEmpty( uuid ) ) result.put( uuid, path );
				}
			}
		}catch( Throwable ex ){
			ex.printStackTrace();
		}
		return result;
	}
	
	public static String toCamelCase( String src ){
		StringBuilder sb = new StringBuilder();
		for( String s : src.split( "_" ) ){
			if( TextUtils.isEmpty( s ) ) continue;
			sb.append( Character.toUpperCase( s.charAt( 0 ) ) );
			sb.append( s.substring( 1, s.length() ).toLowerCase() );
		}
		return sb.toString();
	}
	
	private static DocumentBuilder xml_builder;
	
	public static Element parseXml( byte[] src ){
		if( xml_builder == null ){
			try{
				xml_builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			}catch( Throwable ex ){
				ex.printStackTrace();
				return null;
			}
		}
		try{
			return xml_builder.parse( new ByteArrayInputStream( src ) ).getDocumentElement();
		}catch( Throwable ex ){
			ex.printStackTrace();
			return null;
		}
	}
	
	public static String getAttribute( NamedNodeMap attr_map, String name, String defval ){
		Node node = attr_map.getNamedItem( name );
		if( node != null ) return node.getNodeValue();
		return defval;
	}
	
	@SuppressWarnings("unused")
	public static String formatError( Throwable ex, String fmt, Object... args ){
		if( args.length > 0 ) fmt = String.format( fmt, args );
		return fmt + String.format( " :%s %s", ex.getClass().getSimpleName(), ex.getMessage() );
	}
	
	@SuppressWarnings("unused")
	public static String formatError( Throwable ex, Resources resources, int string_id, Object... args ){
		return resources.getString( string_id, args ) + String.format( " :%s %s", ex.getClass().getSimpleName(), ex.getMessage() );
	}
	
	public static void runOnMainThread( @NonNull Runnable proc ){
		if( Looper.getMainLooper().getThread() == Thread.currentThread() ){
			proc.run();
		}else{
			new Handler( Looper.getMainLooper() ).post( proc );
		}
	}
	
	public static void showToast( final Context context, final boolean bLong, final String fmt, final Object... args ){
		runOnMainThread( new Runnable() {
			@Override
			public void run(){
				Toast.makeText(
					context
					, ( args.length == 0 ? fmt : String.format( fmt, args ) )
					, bLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT
				).show();
			}
		} );
	}
	
	public static void showToast( final Context context, final Throwable ex, final String fmt, final Object... args ){
		runOnMainThread( new Runnable() {
			@Override
			public void run(){
				Toast.makeText(
					context
					, formatError( ex, fmt, args )
					, Toast.LENGTH_LONG
				).show();
			}
		} );
	}
	
	public static void showToast( final Context context, final boolean bLong, final int string_id, final Object... args ){
		runOnMainThread( new Runnable() {
			@Override
			public void run(){
				
				Toast.makeText(
					context
					, context.getString( string_id, args )
					, bLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT
				).show();
			}
		} );
	}
	
	public static void showToast( final Context context, final Throwable ex, final int string_id, final Object... args ){
		runOnMainThread( new Runnable() {
			@Override
			public void run(){
				Toast.makeText(
					context
					, formatError( ex, context.getResources(), string_id, args )
					, Toast.LENGTH_LONG
				).show();
			}
		} );
	}
	
	public static boolean isExternalStorageDocument( Uri uri ){
		return "com.android.externalstorage.documents".equals( uri.getAuthority() );
	}
	
	private static final String PATH_TREE = "tree";
	private static final String PATH_DOCUMENT = "document";
	
	public static String getDocumentId( Uri documentUri ){
		final List< String > paths = documentUri.getPathSegments();
		if( paths.size() >= 2 && PATH_DOCUMENT.equals( paths.get( 0 ) ) ){
			// document
			return paths.get( 1 );
		}
		if( paths.size() >= 4 && PATH_TREE.equals( paths.get( 0 ) )
			&& PATH_DOCUMENT.equals( paths.get( 2 ) ) ){
			// document in tree
			return paths.get( 3 );
		}
		if( paths.size() >= 2 && PATH_TREE.equals( paths.get( 0 ) ) ){
			// tree
			return paths.get( 1 );
		}
		throw new IllegalArgumentException( "Invalid URI: " + documentUri );
	}
	
	public static
	@Nullable
	File getFile( Context context, @NonNull String path ){
		try{
			if( path.startsWith( "/" ) ) return new File( path );
			Uri uri = Uri.parse( path );
			if( "file".equals( uri.getScheme() ) ) return new File( uri.getPath() );
			
			if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ){
				if( isExternalStorageDocument( uri ) ){
					try{
						final String docId = getDocumentId( uri );
						final String[] split = docId.split( ":" );
						if( split.length >= 2 ){
							final String uuid = split[ 0 ];
							if( "primary".equalsIgnoreCase( uuid ) ){
								return new File( Environment.getExternalStorageDirectory() + "/" + split[ 1 ] );
							}else{
								Map< String, String > volume_map = Utils.getSecondaryStorageVolumesMap( context );
								String volume_path = volume_map.get( uuid );
								if( volume_path != null ){
									return new File( volume_path + "/" + split[ 1 ] );
								}
							}
						}
					}catch( Throwable ex2 ){
						ex2.printStackTrace();
					}
				}
			}
			// MediaStore Uri
			Cursor cursor = context.getContentResolver().query( uri, null, null, null, null );
			if( cursor != null ){
				try{
					if( cursor.moveToFirst() ){
						int col_count = cursor.getColumnCount();
						for( int i = 0 ; i < col_count ; ++ i ){
							int type = cursor.getType( i );
							if( type != Cursor.FIELD_TYPE_STRING ) continue;
							String name = cursor.getColumnName( i );
							String value = cursor.isNull( i ) ? null : cursor.getString( i );
							if( ! TextUtils.isEmpty( value ) ){
								if( "filePath".equals( name ) ){
									return new File( value );
								}
							}
						}
					}
				}finally{
					cursor.close();
				}
			}
		}catch( Throwable ex ){
			ex.printStackTrace();
		}
		return null;
	}
	
}
