<?php
// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) exit;

class ms_theme_editor_admin {    
    var $genesis;   
    var $processdone;
    var $childtype;
    var $template;
    var $is_ajax;
    var $is_get;
    var $is_post;
    var $skip_form;
    var $fs;
    var $encoding;

    var $fs_prompt;
    var $fs_method;
    var $uploadsubdir;
    var $menuName;
    var $cache_updates  = TRUE;
    var $debug;
    var $is_debug;
    var $is_new;
    
    var $max_sel;
    var $sel_limit;
    var $mem_limit;
 
    var $themes         = array();
    var $errors         = array();
    var $files          = array();
    var $updates        = array();
    
    var $css;
    var $ui;
   
    var $postarrays     = array(
        'ctc_img',
        'ctc_file_parnt',
        'ctc_file_child',
        'ctc_additional_css',
    );
    var $configfields   = array(
        'theme_parnt', 
        'child_type', 
        'theme_child', 
        'child_template',
       'configtype', 
    );
    var $actionfields   = array(
        'load_styles',
    );
    var $imgmimes       = array(
        'jpg|jpeg|jpe'  => 'image/jpeg',
        'gif'           => 'image/gif',
        'png'           => 'image/png',
    );

    function __construct() {
        $this->processdone  = FALSE;
        $this->genesis      = FALSE;
       
        $this->is_new       = FALSE;
        $this->encoding     = WP_Http_Encoding::is_available();
        $this->menuName     = CHLD_THM_CFG_MENU; 
        $this->is_post      = ( 'POST' == $_SERVER[ 'REQUEST_METHOD' ] );
        $this->is_get       = ( 'GET' == $_SERVER[ 'REQUEST_METHOD' ] );
        $this->is_debug     = get_option( MS_CHILD_THEME_EDITOR . '_debug' );
        $this->debug        = '';
        $this->errors       = array();
    }
  
    function ctc_page_init () {
       
        $this->get_themes();
        $this->childtype = count( $this->themes[ 'child' ] ) ? 'existing' : 'new';
    
        $this->load_config();
      
        do_action( 'chld_thm_cfg_preprocess' );
        do_action( 'chld_thm_cfg_forms' );  
        $this->process_post();
        $this->ui = new ms_theme_editor_ui();
    }
    
    function render() {
        $this->ui->render();
    }

    
    function get( $property, $params = NULL ) {
        return $this->css->get_prop( $property, $params );
    }
    
    function get_themes() {
        
        $this->themes = array( 'child' => array(), 'parnt' => array() );
        foreach ( wp_get_themes() as $theme ):
           
            $group      = $theme->parent() ? 'child' : 'parnt';
           
            $slug       = $theme->get_stylesheet();
            
            $version    = $theme->get( 'Version' );
            
            if ( 'child' == $group ) $version = preg_replace("/\.\d{6}\d+$/", '', $version );
           
            $this->themes[ $group ][ $slug ] = array(
                'Template'      => $theme->get( 'Template' ),
                'Name'          => $theme->get( 'Name' ),
                'ThemeURI'      => $theme->get( 'ThemeURI' ),
                'Author'        => $theme->get( 'Author' ),
                'AuthorURI'     => $theme->get( 'AuthorURI' ),
                'Descr'         => $theme->get( 'Description' ),
                'Tags'          => $theme->get( 'Tags' ),
                'Version'       => $version,
                'screenshot'    => $theme->get_screenshot(),
                'allowed'       => $theme->is_allowed(),
            );
        endforeach;
    }

    function validate_post( $action = 'ms_update', $noncefield = '_wpnonce', $cap = 'install_themes' ) {
       return true;
    }
    
    function load_config() {

        $this->css = new ms_theme_editor_css();
        if ( FALSE !== $this->css->load_config() ):
            $this->debug( 'config exists', __FUNCTION__, __CLASS__, __CLASS__ );
           
            if ( ! $this->check_theme_exists( $this->get( 'child' ) )
                || ! $this->check_theme_exists( $this->get( 'parnt' ) ) ):
                $this->debug( 'theme does not exist', __FUNCTION__, __CLASS__, __CLASS__ );
                
                $this->css = new ms_theme_editor_css();
                $this->css->enqueue = 'enqueue';
            endif;
        else:
            $this->debug( 'config does not exist', __FUNCTION__, __CLASS__, __CLASS__ );
           
            $this->css->enqueue = 'enqueue';
        endif;
        do_action( 'chld_thm_cfg_load' );
        if ( $this->is_get ):
            if ( $this->get( 'child' ) ):
                
                $this->verify_creds();
                $stylesheet = apply_filters( 
                    'chld_thm_cfg_target', 
                    $this->css->get_child_target( $this->get_child_stylesheet() ), 
                    $this->css );
              
                if ( !is_writable( $stylesheet ) && !$this->fs )
                    add_action( 'chld_thm_cfg_admin_notices', array( $this, 'writable_notice' ) );
                if ( $fsize = $this->get( 'fsize' ) ):
                    $test = filesize( $stylesheet );
                    $this->debug( 'filesize saved: ' . $fsize . ' current: ' . $test, __FUNCTION__, __CLASS__, __CLASS__ );
                   
                endif;
              
                if ( !$this->get( 'enqueue' ) ):
                    $this->debug( 'no enqueue:', __FUNCTION__, __CLASS__, __CLASS__ );

                    add_action( 'chld_thm_cfg_admin_notices', array( $this, 'enqueue_notice' ) );     
                endif;
            endif;
         
            if ( fileowner( $this->css->get_child_target( '' ) ) != fileowner( MS_THEME_EDITOR_DIR ) )
                add_action( 'chld_thm_cfg_admin_notices', array( $this, 'owner_notice' ) ); 
        endif;    
    }
    
    function ajax_save_postdata( $action = 'ms_update' ) {
        $this->is_ajax = TRUE;
        $this->debug( 'ajax save ', __FUNCTION__, __CLASS__ );
     
        if ( $this->validate_post( $action ) ):
            if ( 'ctc_plugin' == $action ) do_action( 'chld_thm_cfg_pluginmode' );
            $this->verify_creds(); 
            add_action( 'chld_thm_cfg_cache_updates', array( $this, 'cache_debug' ) );
           
            if ( FALSE !== $this->load_config() ): 
                if ( isset( $_POST[ 'ctc_is_debug' ] ) ):
                   
                    $this->toggle_debug();
                else:
                    $this->css->parse_post_data(); 
                   
                    if ( $this->get( 'child' ) ):
                        
                        do_action( 'chld_thm_cfg_addl_files' );        
						$this->css->write_css();
                    endif;
                    $this->save_config();
                endif;                
                do_action( 'chld_thm_cfg_cache_updates' );
            endif;           
            die( json_encode( $this->css->obj_to_utf8( $this->updates ) ) );
        endif;
        die();
    }
    
    function save_config() {        
        $this->css->save_config();
    }
      
    function ajax_query_css( $action = 'ms_update' ) {
        $this->is_ajax = TRUE;
        if ( $this->validate_post( $action ) ):
            if ( 'ctc_plugin' == $action ) do_action( 'chld_thm_cfg_pluginmode' );
            $this->load_config();
            add_action( 'chld_thm_cfg_cache_updates', array( $this, 'cache_debug' ) );
            $regex = "/^ctc_query_/";
            foreach( preg_grep( $regex, array_keys( $_POST ) ) as $key ):
                $name = preg_replace( $regex, '', $key );
                $param[ $name ] = sanitize_text_field( $_POST[ $key ] );
            endforeach;
            $this->debug( 'ajax params: ' . print_r( $param, TRUE ), __FUNCTION__, __CLASS__, __CLASS__ );
            if ( !empty( $param[ 'obj' ] ) ):
                
                $this->updates[] = array(
                    'key'   => isset( $param[ 'key' ] ) ? $param[ 'key' ] : '',
                    'obj'   => $param[ 'obj' ],
                    'data'  => $this->get( $param[ 'obj' ], $param ),
                );
                do_action( 'chld_thm_cfg_cache_updates' );
                die( json_encode( $this->updates ) );
            endif;
        endif;
        die( 0 );
    }
    
    function process_post() {
       
        if ( $this->is_post ):
		
		    foreach ( $this->actionfields as $field ):
                if ( in_array( 'ctc_' . $field, array_keys( $_POST ) ) ):
                    $actionfield = $field;
                    break;
                endif;
            endforeach;
			
            if ( empty( $actionfield ) ) return FALSE;
            if ( !$this->validate_post( apply_filters( 'chld_thm_cfg_action', 'ms_update' ) ) ):
                $this->errors[] = 2; 
            else:
                $args = preg_grep( "/nonce/", array_keys( $_POST ), PREG_GREP_INVERT );
                $msg = FALSE;
                $this->verify_creds( $args );
                if ( $this->fs )
				{
                    switch( $actionfield )
					{                       
                        case 'load_styles':
                        $msg = $this->ms_create_child_theme();
						break;
                        default:
                        $msg ='';
                    }
                } 
            endif; 
            
            if ( $this->errors ):
                $this->update_redirect( 0 );
          
            elseif ( empty( $this->fs_prompt ) ):
                $this->processdone = TRUE;
                
                $this->update_redirect( $msg );
            endif;
        endif; 
    }
    
    function ms_create_child_theme() {		
		
        $msg = 1;
        $this->is_new = TRUE;        
        foreach ( $this->configfields as $configfield ):
            $varparts = explode( '_', $configfield );
			$varname = end( $varparts );
            ${$varname} = empty( $_POST[ 'ctc_' . $configfield ] ) ? '' : 
                preg_replace( "/\s+/s", ' ', sanitize_text_field( $_POST[ 'ctc_' . $configfield ] ) );
        endforeach;
		
		//Child Theme Parameter		
		$name = sanitize_text_field($_POST['child_name']);//child name		
		$themeuri = sanitize_text_field($_POST['child_theme_uri']);//child theme website
		$author = sanitize_text_field($_POST['child_author']);//child theme website
		$descr = sanitize_text_field($_POST['child_descr']);//child theme website
		$authoruri = sanitize_text_field($_POST['child_author_uri']);//child theme website
		$tags = sanitize_text_field($_POST['child_tags']);//child theme tags
		$version = sanitize_text_field($_POST['child_version']);//child theme version
        
		//child theme handler parameter
		$repairheader = isset($_POST['repairheader']) ? sanitize_text_field($_POST['repairheader']) : '';
        $ignoreparnt = isset($_POST['ignoreparnt']) ? sanitize_text_field($_POST['ignoreparnt']) : '';
        $handling = isset($_POST['handling']) ? sanitize_text_field($_POST['handling']) : '';
        $enqueue = isset($_POST['enqueue']) ? sanitize_text_field($_POST['enqueue']) : '';	
		
		if (isset( $type) )
		{
			$this->childtype = $type;
		}
      
        if ( !$this->is_theme( $configtype ) && $this->is_legacy() ):
            $parnt  = $this->get( 'parnt' );
            $child  = $this->get( 'child' );
            $name   = $this->get( 'child_name' );
        endif;        
       
        if ( $parnt ):
            if ( ! $this->check_theme_exists( $parnt ) ):
                $this->errors[] = '3:' . $parnt; 
            endif;
        else:
            $this->errors[] = 5; 
        endif;

       
        if ( 'new' != $type && empty( $child ) ):
            $this->errors[] = 6;
        elseif ( 'new' == $type || 'duplicate' == $type ):
            if ( empty( $template ) && empty( $name ) ):
                $this->errors[] = 7; 
            else:
                $template_sanitized = preg_replace( "%[^\w\-]%", '', empty( $template ) ? $name : $template );
                if ( $this->check_theme_exists( $template_sanitized ) ):
                    $this->errors[] = '8:' . $template_sanitized; 
                elseif ( 'duplicate' == $type ):
                   
                    $this->clone_child_theme( $child, $template_sanitized );
                    if ( !empty( $this->errors ) ) return FALSE;
                   
                    $this->copy_theme_mods( $child, $template_sanitized );
                    $msg = 3;
                else:
                    $msg = 2;
                endif;
                $child = $template_sanitized;
            endif;
        endif;
            
       if ( FALSE === $this->verify_child_dir( $child ) ):
            
            $this->errors[] = 9;
            return FALSE;
        endif;
		if ( 'reset' == $type ):           
            $this->reset_child_theme();
            $this->enqueue_parent_css();
            $msg = 4;
        else:

            if ( !empty( $this->errors ) ) return FALSE;
            if ( empty( $name ) ):
                $name = ucfirst( $child );
            endif;
			$oldhandling        = $this->get( 'handling' );
           
            $this->css          = new ms_theme_editor_css();
          
            if ( !$this->is_theme( $configtype ) )
                $this->css->set_prop( 'enqueue', 'enqueue' );
            else
            $this->css->set_prop( 'enqueue',            $enqueue );
            $this->css->set_prop( 'handling',           $handling );
            $this->css->set_prop( 'ignoreparnt',        $ignoreparnt );
    
            $this->css->set_prop( 'parnt',              $parnt );
            $this->css->set_prop( 'child',              $child );
            $this->css->set_prop( 'child_name',         $name );
            $this->css->set_prop( 'child_author',       $author );
            $this->css->set_prop( 'child_themeuri',     $themeuri );
            $this->css->set_prop( 'child_authoruri',    $authoruri );
            $this->css->set_prop( 'child_descr',        $descr );
            $this->css->set_prop( 'child_tags',         $tags );
            $this->css->set_prop( 'child_version',      strlen( $version ) ? $version : '1.0' );
    
            if ( isset( $_POST[ 'ctc_action' ] ) && 'plugin' == $_POST[ 'ctc_action' ] ):
                
                $this->css->addl_css = array();
                if ( isset( $_POST[ 'ctc_additional_css' ] ) && is_array( $_POST[ 'ctc_additional_css' ] ) ): 
                    foreach ( $_POST[ 'ctc_additional_css' ] as $file )
                        $this->css->addl_css[] = sanitize_text_field( $file );
                endif;
                add_action( 'chld_thm_cfg_parse_stylesheets', array( $this, 'parse_child_stylesheet_to_target' ) );
            elseif ( isset( $_POST['ms_theme_child_analysis'] )):
               
                $this->evaluate_signals();
            endif;
            
            
            $this->css->forcedep = array();
            if ( isset( $_POST[ 'ctc_forcedep' ] ) && is_array( $_POST[ 'ctc_forcedep' ] ) ): 
                foreach ( $_POST[ 'ctc_forcedep' ] as $handle )
                    $this->css->forcedep[ sanitize_text_field( $handle ) ] = 1;
            endif;

            
            if ( $this->genesis ):
                $handling       = 'separate';
                $enqueue        = 'none';
                $ignoreparnt    = TRUE;
                if ( $this->backup_or_restore_file( 'ms-separate-style.css', TRUE, 'style.css' ) &&
                    $this->backup_or_restore_file( 'style.css', TRUE, 'ctc-genesis.css' ) ):
                    $this->delete_child_file( 'ctc-genesis', 'css' );
                else:
                    $this->errors[] = 10; 
                endif;
            endif;
            
            if ( !empty( $this->errors ) ) return FALSE;   
           
            if ( 'enqueue' == $enqueue && ( $this->get( 'parntloaded' ) || !$this->get( 'hasstyles' ) || $ignoreparnt ) ) $enqueue = 'none';       
            
            if ( $this->is_theme( $configtype ) || $this->is_legacy()):                
                
                if ( $this->get( 'hasstyles' ) && !$ignoreparnt ):
                    $this->debug( 'Adding action: parse_parent_stylesheet_to_source', __FUNCTION__, __CLASS__ );
                    add_action( 'chld_thm_cfg_parse_stylesheets', array( $this, 'parse_parent_stylesheet_to_source' ) );
                endif;               
                
                if ( is_multisite())
                    add_action( 'chld_thm_cfg_addl_options', array( $this, 'network_enable' ) );
            endif;
            $this->debug( 'Adding action: parse_additional_stylesheets_to_source', __FUNCTION__, __CLASS__ );
            add_action( 'chld_thm_cfg_parse_stylesheets', array( $this, 'parse_additional_stylesheets_to_source' ) );
        
            if ( 'separate' == $handling ):               
                
                add_action( 'chld_thm_cfg_parse_stylesheets', array( $this, 'parse_child_stylesheet_to_source' ) );
                $this->debug( 'Adding action: parse_custom_stylesheet_to_target', __FUNCTION__, __CLASS__ );
                add_action( 'chld_thm_cfg_parse_stylesheets', array( $this, 'parse_custom_stylesheet_to_target' ) );
            elseif ( 'primary' == $handling ):
               
                add_action( 'chld_thm_cfg_parse_stylesheets', array( $this, 'parse_child_stylesheet_to_target' ) );
                if ( $oldhandling != $handling ):
                    $this->debug( 'Adding action: parse_custom_stylesheet_to_target', __FUNCTION__, __CLASS__ );
                    add_action( 'chld_thm_cfg_parse_stylesheets', array( $this, 'parse_custom_stylesheet_to_target' ) );
                endif;
            endif;           
            
            if ( $this->is_theme( $configtype )):                
                add_action( 'chld_thm_cfg_addl_files', array( $this, 'add_base_files' ), 10, 2 );
                add_action( 'chld_thm_cfg_addl_files', array( $this, 'copy_screenshot' ), 10, 2 );
                add_action( 'chld_thm_cfg_addl_files', array( $this, 'enqueue_parent_css' ), 15, 2 );
                if ( $repairheader && 'reset' != $type ):
                    add_action( 'chld_thm_cfg_addl_files', array( $this, 'repair_header' ) );
                endif;
            endif;    
            do_action( 'chld_thm_cfg_parse_stylesheets' );
            if ( isset( $_POST[ 'ctc_parent_mods' ] ) && 'duplicate' != $type )
                $this->copy_theme_mods( $parnt, $child );
            $this->enqueue_parent_css( TRUE );
            do_action( 'chld_thm_cfg_addl_files' );
            
            if ( !empty ( $this->errors ) ) return FALSE;
            
            if ( 'separate' == $handling ):
                $this->debug( 'Writing new stylesheet header...', __FUNCTION__, __CLASS__ );
                $this->rewrite_stylesheet_header();
            endif;
           
            $this->css->set_prop( 'converted', 1 );
            
          
            $this->debug( 'Writing new CSS...', __FUNCTION__, __CLASS__ );
            if ( FALSE === $this->css->write_css() ):
                
                $this->errors[] = 11; 
                return FALSE;
            endif; 
            
            $this->get_files( $parnt );
        endif;       
        $this->save_config();
       do_action( 'chld_thm_cfg_addl_options' );
       return $msg;
    }
    
    function sanitize_options( $input ) {
        return $input;
    }   
    
    function sanitize_slug( $slug ) {
        return preg_replace( "/[^\w\-]/", '', $slug );
    }    
    function update_redirect( $msg = 1 ) {
        $this->log_debug();
        if ( empty( $this->is_ajax ) ):
            $ctcpage = apply_filters( 'chld_thm_cfg_admin_page', CHLD_THM_CFG_MENU );
            $screen = get_current_screen()->id;
            wp_safe_redirect(
                ( strstr( $screen, '-network' ) ? network_admin_url( 'themes.php' ) : admin_url( 'admin.php' ) ) 
                    . '?page=' . $ctcpage . ( $msg ? '&updated=' . $msg : ( $this->errors ? '&error=' . implode( ',', $this->errors ) : '' ) ) );
            die();
        endif;
    }
    
    function verify_child_dir( $path ) {
        $this->debug( 'Verifying child dir: ' . $path, __FUNCTION__, __CLASS__ );
        if ( !$this->fs ): 
            $this->debug( 'No filesystem access.', __FUNCTION__, __CLASS__ );
            return FALSE; 
        endif;
        global $wp_filesystem;
        $themedir = $wp_filesystem->find_folder( get_theme_root() );
        if ( ! $wp_filesystem->is_writable( $themedir ) ):
            $this->debug( 'Directory not writable: ' . $themedir, __FUNCTION__, __CLASS__ );
            return FALSE;
        endif;
        $childparts = explode( '/', $this->normalize_path( $path ) );
        while ( count( $childparts ) ):
            $subdir = array_shift( $childparts );
            if ( empty( $subdir ) ) continue;
            $themedir = trailingslashit( $themedir ) . $subdir;
            if ( ! $wp_filesystem->is_dir( $themedir ) ):
                if ( ! $wp_filesystem->mkdir( $themedir, FS_CHMOD_DIR ) ):
                $this->debug( 'Could not make directory: ' . $themedir, __FUNCTION__, __CLASS__ );
                    return FALSE;
                endif;
            elseif ( ! $wp_filesystem->is_writable( $themedir ) ):
                $this->debug( 'Directory not writable: ' . $themedir, __FUNCTION__, __CLASS__ );
                return FALSE;
            endif;
        endwhile;
        $this->debug( 'Child dir verified: ' . $themedir, __FUNCTION__, __CLASS__ );
        return TRUE;
    }
    
    function add_base_files( $obj ){
        
        $contents = "<?php
// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) exit;
";
        $handling = $this->get( 'handling' );
        $this->write_child_file( 'functions.php', $contents );
        $this->backup_or_restore_file( 'style.css' );
        $contents = $this->css->get_css_header_comment( $handling );
        $this->debug( 'writing initial stylesheet header...' . LF . $contents, __FUNCTION__, __CLASS__ );
        $this->write_child_file( 'style.css', $contents );
        if ( 'separate' == $handling ):
            $this->backup_or_restore_file( 'ms-separate-style.css' );
            $this->write_child_file( 'ms-separate-style.css', $contents . LF );
        endif;
    }
    
   
    function convert_import_to_enqueue( $import, $count, $execute = FALSE ) {
        $relpath    = $this->get( 'child' );
        $import     = preg_replace( "#^.*?url\(([^\)]+?)\).*#", "$1", $import );
        $import     = preg_replace( "#[\'\"]#", '', $import );
        $path       = $this->css->convert_rel_url( trim( $import ), $relpath , FALSE );
        $abs        = preg_match( '%(https?:)?//%', $path );
        if ( $execute )
            wp_enqueue_style( 'chld_thm_cfg_ext' . $count,  $abs ? $path : trailingslashit( get_theme_root_uri() ) . $path );
        else
            return "wp_enqueue_style( 'chld_thm_cfg_ext" . $count . "', " 
                . ( $abs ? "'" . $path . "'" : "trailingslashit( get_theme_root_uri() ) . '" . $path . "'" ) . ' );';
    }    
   
    function convert_enqueue_to_import( $path ) {
        if ( preg_match( '%(https?:)?//%', $path ) ):
            $this->css->imports[ 'child' ]['@import url(' . $path . ')'] = 1;
            return;
        endif;
        $regex  = '#^' . preg_quote( trailingslashit( $this->get( 'child' ) ) ) . '#';
        $path   = preg_replace( $regex, '', $path, -1, $count );
        if ( $count ): 
            $this->css->imports[ 'child' ]['@import url(' . $path . ')'] = 1;
            return;
        endif;
        $parent = trailingslashit( $this->get( 'parnt' ) );
        $regex  = '#^' . preg_quote( $parent ) . '#';
        $path   = preg_replace( $regex, '../' . $parent, $path, -1, $count );
        if ( $count )
            $this->css->imports[ 'child' ]['@import url(' . $path . ')'] = 1;
    }   
    
    function enqueue_parent_code(){
       
        $imports        = $this->get( 'imports' );
        $enqueues       = array();
        $code           = "// AUTO GENERATED - Do not modify or remove comment markers above or below:" . LF;
        $deps           = $this->get( 'parnt_deps' );
        $enq            = $this->get( 'enqueue' );
        $handling       = $this->get( 'handling' );
        $hasstyles      = $this->get( 'hasstyles' );
        $childloaded    = $this->get( 'childloaded' );
        $parntloaded    = $this->get( 'parntloaded' );
        $cssunreg       = $this->get( 'cssunreg' );
        $csswphead      = $this->get( 'csswphead' );
        $cssnotheme     = $this->get( 'cssnotheme' );
        $ignoreparnt    = $this->get( 'ignoreparnt' );
        $priority       = $this->get( 'qpriority' );
        $reorder        = $this->get( 'reorder' );
        $this->debug( 'forcedep: ' . print_r( $this->get( 'forcedep' ), TRUE ) . ' deps: ' . print_r( $deps, TRUE ) . ' enq: ' . $enq . ' handling: ' . $handling
            . ' hasstyles: ' . $hasstyles . ' parntloaded: ' . $parntloaded . ' childloaded: ' . $childloaded . ' reorder: ' . $reorder
            . ' ignoreparnt: ' . $ignoreparnt . ' priority: ' . $priority . ' childtype: ' . $this->childtype, __FUNCTION__, __CLASS__ );
        
        if ( 'enqueue' == $enq && $hasstyles && !$parntloaded && !$ignoreparnt ):
            
            $deps = array_diff( $deps, array( 'chld_thm_cfg_parent' ) );
            $code .= "
if ( !function_exists( 'ms_theme_editor_parent_css' ) ):
    function ms_theme_editor_parent_css() {
        wp_enqueue_style( 'chld_thm_cfg_parent', trailingslashit( get_template_directory_uri() ) . 'style.css', array( " . implode( ',', $deps ) . " ) );
    }
endif;
add_action( 'wp_enqueue_scripts', 'ms_theme_editor_parent_css', " . $priority . " );
";
            $deps = array( "'chld_thm_cfg_parent'" );
        endif;        
        if ( 'separate' != $handling && $childloaded && $reorder && ( $parntloaded || in_array( 'chld_thm_cfg_parent', $deps ) ) ):
            $dephandle = $parntloaded ? $parntloaded : 'chld_thm_cfg_parent';
            $code .= "
if ( !function_exists( 'ms_theme_editor_parent_dep') ):
function ms_theme_editor_parent_dep() {
    global \$wp_styles;
    array_unshift( \$wp_styles->registered[ '" . $childloaded . "' ]->deps, '" . $dephandle . "' );
}
endif;
add_action( 'wp_head', 'ms_theme_editor_parent_dep', 2 );
";
        endif;      
        if ( !empty( $imports ) ):
            $ext = 0;
            foreach ( $imports as $import ):
                if ( !empty( $import ) ):
                    $ext++;
                    $enqueues[] = '        ' . $this->convert_import_to_enqueue( $import, $ext ); 
                endif;
            endforeach;
        endif;       
        
      if ( 'separate' != $handling && ( ( $csswphead || $cssunreg || $cssnotheme ) 
            || ( 'new' != $this->childtype && !$childloaded ) 
            ) ): 
            $deps = array_merge( $deps, $this->get( 'child_deps' ) );
           
            $deps = array_diff( $deps, array( 'chld_thm_cfg_child' ) );
            $enqueues[] = "        wp_enqueue_style( 'chld_thm_cfg_child', trailingslashit( get_stylesheet_directory_uri() ) . 'style.css', array( " . implode( ',', $deps ) . " ) );";
           
            $deps = array( "'chld_thm_cfg_child'" );
        endif;
        if ( 'separate' == $handling ):
            $deps = array_merge( $deps, $this->get( 'child_deps' ) );
           
            $deps = array_diff( $deps, array( 'ms_theme_editor_child_ms_separate' ) );
            $enqueues[] = "        wp_enqueue_style( 'ms_theme_editor_child_ms_separate', trailingslashit( get_stylesheet_directory_uri() ) . 'ms-separate-style.css', array( " . implode( ',', $deps ) . " ) );";
        endif;
        if ( count( $enqueues ) ):
            $code .= "         
if ( !function_exists( 'ms_theme_editor_child_css' ) ):
    function ms_theme_editor_child_css() {" . LF;
            $code .= implode( "\n", $enqueues );
            $code .= "
    }
endif;
add_action( 'wp_enqueue_scripts', 'ms_theme_editor_child_css', " . ( $priority + 10 ) . " );" . LF;
        endif;
        if ( $ignoreparnt )
            $code .= "
defined( 'CHLD_THM_CFG_IGNORE_PARENT' ) or define( 'CHLD_THM_CFG_IGNORE_PARENT', TRUE );" . LF;
       
        return explode( "\n", $code ); 
    }   
    
    function enqueue_parent_css( $getexternals = FALSE ) {
        $this->debug( 'enqueueing parent css: getexternals = ' . $getexternals, __FUNCTION__, __CLASS__ );
        $marker  = 'ENQUEUE PARENT ACTION';
        $insertion  =  $this->enqueue_parent_code();
        if ( $filename   = $this->css->is_file_ok( $this->css->get_child_target( 'functions.php' ), 'write' ) ):
            $this->insert_with_markers( $filename, $marker, $insertion, $getexternals );
           
            if ( !$getexternals && 'reset' == $this->childtype ):
                $marker  = 'CTC ENQUEUE PLUGIN ACTION';
                $this->insert_with_markers( $filename, $marker, array() );
            endif;
        endif;
    }   
  
    function insert_with_markers( $filename, $marker, $insertion, $getexternals = FALSE ) { 
        if ( count( $this->errors ) ):
            $this->debug( 'Errors detected, returning', __FUNCTION__, __CLASS__ );
            return FALSE;
        endif;
       
        if ( $this->is_ajax && is_readable( $filename ) && is_writable( $filename ) ):
            
            $this->debug( 'Ajax update, bypassing wp filesystem.', __FUNCTION__, __CLASS__ );
            $markerdata = explode( "\n", @file_get_contents( $filename ) );
        elseif ( !$this->fs ): 
            $this->debug( 'No filesystem access.', __FUNCTION__, __CLASS__ );
            return FALSE; 
        else:
            global $wp_filesystem;
            if( !$wp_filesystem->exists( $this->fspath( $filename ) ) ):
                if ( $getexternals ):
                    $this->debug( 'Read only and no functions file yet, returning...', __FUNCTION__, __CLASS__ );
                    return FALSE;
                else:
                    
                    $this->debug( 'No functions file, creating...', __FUNCTION__, __CLASS__ );
                    $this->add_base_files( $this );
                endif;
            endif;
            
            $markerdata = explode( "\n", $wp_filesystem->get_contents( $this->fspath( $filename ) ) );
        endif;
        $newfile = '';
        $externals  = array();
        $phpopen    = 0;
        $in_comment = 0;
        $foundit = FALSE;
        if ( $markerdata ):
            $state = TRUE;
            foreach ( $markerdata as $n => $markerline ):
               
                $str = preg_replace( "/\/\/.*$/", '', $markerline );
                preg_match_all("/(<\?|\?>|\*\/|\/\*)/", $str, $matches );
                if ( $matches ):
                    foreach ( $matches[1] as $token ): 
                        if ( '/*' == $token ):
                            $in_comment = 1;
                        elseif ( '*/' == $token ):
                            $in_comment = 0;
                        elseif ( '<?' == $token && !$in_comment ):
                            $phpopen = 1;
                        elseif ( '?>' == $token && !$in_comment ):
                            $phpopen = 0;
                        endif;
                    endforeach;
                endif;
                if ( strpos( $markerline, '// BEGIN ' . $marker ) !== FALSE )
                    $state = FALSE;
                if ( $state ):
                    if ( $n + 1 < count( $markerdata ) )
                        $newfile .= "{$markerline}\n";
                    else
                        $newfile .= "{$markerline}";
                elseif ( $getexternals ):
                   
                    if ( preg_match( "/wp_enqueue_style.+?'chld_thm_cfg_ext\d+'.+?'(.+?)'/", $markerline, $matches ) ):
                        $this->debug( 'external link found : ' . $matches[ 1 ] );
                        $this->convert_enqueue_to_import( $matches[ 1 ] );
                    endif;
                endif;
                if ( strpos( $markerline, '// END ' . $marker ) !== FALSE ):
                    if ( 'reset' != $this->childtype ):
                        $newfile .= "// BEGIN {$marker}\n";
                        if ( is_array( $insertion ) )
                            foreach ( $insertion as $insertline )
                                $newfile .= "{$insertline}\n";
                        $newfile .= "// END {$marker}\n";
                    endif;
                    $state = TRUE;
                    $foundit = TRUE;
                endif;
            endforeach;
        else:
            $this->debug( 'Could not parse functions file', __FUNCTION__, __CLASS__ );
            return FALSE;
        endif;
        if ( $foundit ):
            $this->debug( 'Found marker, replaced inline', __FUNCTION__, __CLASS__ );
        else:
            if ( 'reset' != $this->childtype ):
                
                if ( ! $phpopen ):
                    $this->debug( 'PHP not open', __FUNCTION__, __CLASS__ );
                  
                    $newfile .= '<?php' . LF;
                endif;
                $newfile .= "\n// BEGIN {$marker}\n";
                foreach ( $insertion as $insertline )
                    $newfile .= "{$insertline}\n";
                $newfile .= "// END {$marker}\n";
            endif;
        endif;
        
        if ( $getexternals ):
            $this->debug( 'Read only, returning.', __FUNCTION__, __CLASS__ );
        else:
            $mode = 'direct' == $this->fs_method ? FALSE : 0666;
            $this->debug( 'Writing new functions file...', __FUNCTION__, __CLASS__ );
            if ( $this->is_ajax && is_writable( $filename ) ): 
              
                if ( FALSE === @file_put_contents( $filename, $newfile ) ): 
                    $this->debug( 'Ajax write failed.', __FUNCTION__, __CLASS__ );
                    return FALSE;
                endif;
            elseif ( FALSE === $wp_filesystem->put_contents( 
                $this->fspath( $filename ), 
                $newfile, 
                $mode 
            ) ): 
                $this->debug( 'Filesystem write failed.', __FUNCTION__, __CLASS__ );
                return FALSE;
            endif;
            $this->css->set_prop( 'converted', 1 );
        endif;
    }   
    
    function write_child_file( $file, $contents ) {
        
        if ( !$this->fs ): 
            $this->debug( 'No filesystem access, returning.', __FUNCTION__, __CLASS__ );
            return FALSE; 
        endif;
        global $wp_filesystem;
        if ( $file = $this->css->is_file_ok( $this->css->get_child_target( $file ), 'write' ) ):
            $mode = 'direct' == $this->fs_method ? FALSE : 0666;
            $file = $this->fspath( $file );
            if ( $wp_filesystem->exists( $file ) ):
                $this->debug( 'File exists, returning.', __FUNCTION__, __CLASS__ );
                return FALSE;
            else:
                $this->debug( 'Writing to filesystem: ' . $file . LF . $contents, __FUNCTION__, __CLASS__ );
                if ( FALSE === $wp_filesystem->put_contents( 
                    $file, 
                    $contents,
                    $mode 
                    ) ):
                    $this->debug( 'Filesystem write failed, returning.', __FUNCTION__, __CLASS__ );
                    return FALSE; 
                endif;
            endif;
        else:
            $this->debug( 'No directory, returning.', __FUNCTION__, __CLASS__ );
            return FALSE;
        endif;
        $this->debug( 'Filesystem write successful.', __FUNCTION__, __CLASS__ );
    }
    function copy_screenshot() {        
        $this->copy_parent_file( 'screenshot' ); 
    }
    
    function copy_parent_file( $file, $ext = 'php' ) {
        
        if ( !$this->fs ): 
            $this->debug( 'No filesystem access.', __FUNCTION__, __CLASS__ );
            return FALSE; 
        endif;
        global $wp_filesystem;
        $parent_file = NULL;
        if ( 'screenshot' == $file ):
            foreach ( array_keys( $this->imgmimes ) as $extreg ): 
                foreach( explode( '|', $extreg ) as $ext )
                    if ( ( $parent_file = $this->css->is_file_ok( $this->css->get_parent_source( 'screenshot.' . $ext ) ) ) ) 
                        break;
                if ( $parent_file ):
                    $parent_file = $this->fspath( $parent_file );
                    break;
                endif;
            endforeach;
            if ( !$parent_file ):
                $this->debug( 'No screenshot found.', __FUNCTION__, __CLASS__ );
                return;
            endif;
        else:
            $parent_file = $this->fspath( $this->css->is_file_ok( $this->css->get_parent_source( $file . '.' . $ext ) ) );
        endif;
        
        
        $child_file = $this->css->get_child_target( $file . '.' . $ext );
        
        if ( $wp_filesystem->exists( $this->fspath( $child_file ) ) ) return TRUE;
        $child_dir = dirname( $this->theme_basename( '', $child_file ) );
        $this->debug( 'Verifying child dir... ', __FUNCTION__, __CLASS__ );
        if ( $parent_file 
            && $child_file 
                && $this->verify_child_dir( $child_dir ) 
                    && $wp_filesystem->copy( $parent_file, $this->fspath( $child_file ), FS_CHMOD_FILE ) ):
            $this->debug( 'Filesystem copy successful', __FUNCTION__, __CLASS__ );
            return TRUE;
        endif;
        
        $this->errors[] = '13:' . $parent_file; 
    }
    
    function delete_child_file( $file, $ext = 'php' ) {
        if ( !$this->fs ): 
            $this->debug( 'No filesystem access.', __FUNCTION__, __CLASS__ );
            return FALSE; 
        endif;
        global $wp_filesystem;
        
        $file = ( 'img' == $ext ? $file : $file . '.' . $ext );
        if ( $child_file  = $this->css->is_file_ok( $this->css->get_child_target( $file ), 'write' ) ):
            if ( $wp_filesystem->exists( $this->fspath( $child_file ) ) ):
                
                if ( $wp_filesystem->delete( $this->fspath( $child_file ) ) ):
                    return TRUE;
                else:
                
                    $this->errors[] = '14:' . $ext;
                    $this->debug( 'Could not delete ' . $ext . ' file', __FUNCTION__, __CLASS__ );
        
                endif;
            endif;
        endif;
    }
    
    function get_files( $theme, $type = 'template' ) {
        $isparent = ( $theme === $this->get( 'parnt' ) );
        if ( 'template' == $type && $isparent && ( $templates = $this->get( 'templates' ) ) ): 
            return $templates;
        elseif ( !isset( $this->files[ $theme ] ) ):

            $this->files[ $theme ] = array();
            $imgext = '(' . implode( '|', array_keys( $this->imgmimes ) ) . ')';
            foreach ( $this->css->recurse_directory(
                trailingslashit( get_theme_root() ) . $theme, '', TRUE ) as $filepath ):
                $file = $this->theme_basename( $theme, $filepath );
                if ( preg_match( "/^style\-(\d+)\.css$/", $file, $matches ) ):
                    $date = date_i18n( 'D, j M Y g:i A', strtotime( $matches[ 1 ] ) );
                    $this->files[ $theme ][ 'backup' ][ $file ] = $date;
                   
                elseif ( strstr( $file, "msbackup" ) ):
                    $date = date_i18n( 'D, j M Y g:i A', filemtime( $filepath ) );
                    $this->files[ $theme ][ 'backup' ][ $file ] = $date;               
                   
                elseif ( preg_match( "/\.php$/", $file ) ):
                    if ( $isparent ):
                    
                        if ( ( $file_verified = $this->css->is_file_ok( $this->css->get_parent_source( $file, $theme ) , 'read' ) ) ):
                            $this->debug( 'scanning ' . $file_verified . '... ', __FUNCTION__, __CLASS__ );
                            
                            $template = FALSE;
                            $size = 0;
                            if ( $handle = fopen( $file_verified, "rb") ):
                                while ( !feof( $handle ) ):
                                    $size++;
                                    if ( $size > 10 ) 
                                        break;
                                    $contents = fread($handle, 2048);
                                    if ( preg_match( "/\w+\s*\(/", $contents ) ):
                                        $template = TRUE;
                                        
                                        $contents = preg_replace( "%<script>.+?</script>%s", '', $contents );
                                        $contents = preg_replace( "%(^.+?</script>|<script>.+$)%s", '', $contents );
                                        
                                        if ( preg_match( "/(function \w+?|require(_once)?)\s*\(/", $contents ) ):
                                            $this->debug( 'disqualifying code found in chunk ' . $size, __FUNCTION__, __CLASS__ );
                                            $template = FALSE;
                                            break;
                                        endif;
                                    endif;
                                endwhile;
                                fclose( $handle );
                            endif;
                            if ( $template )
                                $this->files[ $theme ][ 'template' ][] = $file;
                        endif;
                    else:
                       
                        $this->files[ $theme ][ 'template' ][] = $file;
                    endif;
                elseif ( preg_match( "/\.css$/", $file ) 
                    && ( !in_array( $file, array( 
                        'style.css', 
                        'ms-separate-style.css'                         
                    ) ) ) ):
                    $this->files[ $theme ][ 'stylesheet' ][] = $file;
                    
                elseif ( preg_match( "/\.(js|txt)$/", $file ) ):
                    $this->files[ $theme ][ 'txt' ][] = $file;
                elseif ( preg_match( "/^images\/.+?\." . $imgext . "$/", $file ) ):
                    $this->files[ $theme ][ 'img' ][] = $file;
                   
                else:
                    $this->files[ $theme ][ 'other' ][] = $file;
                endif;
            endforeach;
        endif;
        if ( $isparent ):
            
            $this->css->templates = $this->files[ $theme ][ 'template' ];
        endif;
        $types = explode( ",", $type );
        $files = array();
        foreach ( $types as $type ):
            if ( isset( $this->files[ $theme ][ $type ] ) )
                $files = array_merge( $this->files[ $theme ][ $type ], $files );
        endforeach;
        return $files;
    }
        
    function theme_basename( $theme, $file ) {
        $file = $this->normalize_path( $file );
       
        $themedir = trailingslashit( $this->normalize_path( get_theme_root() ) ) . ( '' == $theme ? '' : trailingslashit( $theme ) );
        
        return preg_replace( '%^' . preg_quote( $themedir ) . '%', '', $file );
    }
    
    function uploads_basename( $file ) {
        $file = $this->normalize_path( $file );
        $uplarr = wp_upload_dir();
        $upldir = trailingslashit( $this->normalize_path( $uplarr[ 'basedir' ] ) );
        return preg_replace( '%^' . preg_quote( $upldir ) . '%', '', $file );
    }
    
    function uploads_fullpath( $file ) {
        $file = $this->normalize_path( $file );
        $uplarr = wp_upload_dir();
        $upldir = trailingslashit( $this->normalize_path( $uplarr[ 'basedir' ] ) );
        return $upldir . $file;
    }
    
    function serialize_postarrays() {
        foreach ( $this->postarrays as $field )
            if ( isset( $_POST[ $field ] ) && is_array( $_POST[ $field ] ) )
                $_POST[ $field ] = implode( "%%", $_POST[ $field ] );
    }
    
    function unserialize_postarrays() {
        foreach ( $this->postarrays as $field )
            if ( isset( $_POST[ $field ] ) && !is_array( $_POST[ $field ] ) )
                $_POST[ $field ] = explode( "%%", $_POST[ $field ] );
    }
    
    function set_writable( $file = NULL ) {

        if ( isset( $file ) ):
            $file =  $this->css->get_child_target( $file . '.php' );
        else:
            $file =  apply_filters( 'chld_thm_cfg_target', $this->css->get_child_target( 'separate' == $this->get( 'handling' ) ? 'ms-separate-style.css' : 'style.css' ), $this->css );
        endif;
        if ( $this->fs ): 
            if ( is_writable( $file ) ) return;
            global $wp_filesystem;
            if ( $file && $wp_filesystem->chmod( $this->fspath( $file ), 0666 ) ) 
                return;
        endif;
        $this->errors[] = 28;
        return FALSE;
    }
    
    function clone_child_theme( $child, $clone ) {
        if ( !$this->fs ) return FALSE; 
        global $wp_filesystem;
       
        $this->css->set_prop( 'child', $child );

        $dir        = untrailingslashit( $this->css->get_child_target( '' ) );
        $themedir   = trailingslashit( get_theme_root() );
        $fsthemedir = $this->fspath( $themedir );
        $files = $this->css->recurse_directory( $dir, NULL, TRUE );
        $errors = array();
        foreach ( $files as $file ):
            $childfile  = $this->theme_basename( $child, $this->normalize_path( $file ) );
            $newfile    = trailingslashit( $clone ) . $childfile;
            $childpath  = $fsthemedir . trailingslashit( $child ) . $childfile;
            $newpath    = $fsthemedir . $newfile;
            $this->debug( 'Verifying child dir... ', __FUNCTION__, __CLASS__ );
            if ( $this->verify_child_dir( is_dir( $file ) ? $newfile : dirname( $newfile ) ) ):
                if ( is_file( $file ) && !@$wp_filesystem->copy( $childpath, $newpath ) ):
                    $this->errors[] = '15:' . $newpath; 
                endif;
            else:
                $this->errors[] = '16:' . $newfile; 
            endif;
        endforeach;
    }

    function unset_writable() {
        if ( !$this->fs ) return FALSE; 
        global $wp_filesystem;
        $dir        = untrailingslashit( $this->css->get_child_target( '' ) );
        $child      = $this->theme_basename( '', $dir );
        $newchild   = untrailingslashit( $child ) . '-new';
        $themedir   = trailingslashit( get_theme_root() );
        $fsthemedir = $this->fspath( $themedir );
        
        if ( fileowner( $dir ) == fileowner( $themedir ) ):
            $copy   = FALSE;
            $wp_filesystem->chmod( $dir );
            
        else:
            $copy   = TRUE;
        endif;
        
        $files = $this->css->recurse_directory( $dir, NULL, TRUE );
        $errors = array();
        foreach ( $files as $file ):
            $childfile  = $this->theme_basename( $child, $this->normalize_path( $file ) );
            $newfile    = trailingslashit( $newchild ) . $childfile;
            $childpath  = $fsthemedir . trailingslashit( $child ) . $childfile;
            $newpath    = $fsthemedir . $newfile;
            if ( $copy ):
                $this->debug( 'Verifying child dir... ' . $file, __FUNCTION__, __CLASS__ );
                if ( $this->verify_child_dir( is_dir( $file ) ? $newfile : dirname( $newfile ) ) ):
                    if ( is_file( $file ) && !$wp_filesystem->copy( $childpath, $newpath ) ):
                        $errors[] = '15:' . $newpath; 
                    endif;
                else:
                    $errors[] = '16:' . $newfile; 
                endif;
            else:
                $wp_filesystem->chmod( $this->fspath( $file ) );
            endif;
        endforeach;
        if ( $copy ):
            
            $newfiles = $this->css->recurse_directory( trailingslashit( $themedir ) . $newchild, NULL, TRUE );
            $deleteddirs = $deletedfiles = 0;
            if ( count( $newfiles ) == count( $files ) ):
              
                if ( !$wp_filesystem->exists( trailingslashit( $fsthemedir ) . $child . '-old' ) )
                    $wp_filesystem->move( trailingslashit( $fsthemedir ) . $child, trailingslashit( $fsthemedir ) . $child . '-old' );
                
                if ( !$wp_filesystem->exists( trailingslashit( $fsthemedir ) . $child ) )
                    $wp_filesystem->move( trailingslashit( $fsthemedir ) . $newchild, trailingslashit( $fsthemedir ) . $child );
                
                $oldfiles = $this->css->recurse_directory( trailingslashit( $themedir ) . $child . '-old', NULL, TRUE );
                array_unshift( $oldfiles, trailingslashit( $themedir ) . $child . '-old' );
                foreach ( array_reverse( $oldfiles ) as $file ):
                    if ( $wp_filesystem->delete( $this->fspath( $file ) ) 
                        || ( is_dir( $file ) && @rmdir( $file ) ) 
                            || ( is_file( $file ) && @unlink( $file ) ) ):
                        $deletedfiles++;
                    endif;
                endforeach;
                if ( $deletedfiles != count( $oldfiles ) ):
                    $errors[] = '17:' . $deletedfiles . ':' . count( $oldfiles ); 
                endif;
            else:
                $errors[] = 18; 
            endif;
        endif;
        if ( count( $errors ) ):
            $this->errors[] = 19; 
        endif;
    }
    
    function verify_creds( $args = array() ) {
        $this->fs_prompt = $this->fs = FALSE;        
        $this->serialize_postarrays();        
        $ctcpage = apply_filters( 'chld_thm_cfg_admin_page', CHLD_THM_CFG_MENU );
        $url = is_multisite() ?  network_admin_url( 'themes.php?page=' . $ctcpage ) :
            admin_url( 'admin.php?page=' . $ctcpage );
        $nonce_url = wp_nonce_url( $url, apply_filters( 'chld_thm_cfg_action', 'ms_update' ), '_wpnonce' );
        ob_start();
        if ( $creds = request_filesystem_credentials( $nonce_url, '', FALSE, FALSE, $args ) ):           
            if ( WP_Filesystem( $creds ))                
                $this->fs = TRUE;
            else                
                $creds = request_filesystem_credentials( $nonce_url, '', TRUE, FALSE, $args );
        else:            
            WP_Filesystem();
        endif;
       
        $this->fs_prompt = ob_get_clean();
        $this->debug( 'FS: ' . $this->fs . ' PROMPT: ' . $this->fs_prompt, __FUNCTION__, __CLASS__ );
       $this->unserialize_postarrays();
    }    
    
    function fspath( $file ){
        if ( ! $this->fs ) return FALSE; // return if no filesystem access
        global $wp_filesystem;
        if ( is_dir( $file ) ):
            $dir = $file;
            $base = '';
        else:
            $dir = dirname( $file );
            $base = basename( $file );
        endif;
        $fsdir = $wp_filesystem->find_folder( $dir );
        return trailingslashit( $fsdir ) . $base;
    }       
   
    function normalize_path( $path ) {
        $path = str_replace( '\\', '/', $path );        
        $path = substr( $path, 0, 1 ) . preg_replace( '|/+|','/', substr( $path, 1 ) );
        if ( ':' === substr( $path, 1, 1 ) )
            $path = ucfirst( $path );
        return $path;
    }
    
    function check_theme_exists( $theme ) {
        $search_array = array_map( 'strtolower', array_keys( wp_get_themes() ) );
        return in_array( strtolower( $theme ), $search_array );
    }   
    
    function is_legacy() {
        return defined('CHLD_THM_CFG_PLUGINS_VERSION') 
            && version_compare( CHLD_THM_CFG_PLUGINS_VERSION, '2.0.0', '<' );
    }
    
    function is_theme( $configtype = '' ) {
       $pluginmode = apply_filters( 'chld_thm_cfg_action', NULL );
        if ( $pluginmode || ( !empty( $configtype ) && 'theme' != $configtype ) ):
            return FALSE;
        endif;
        if ( $this->is_legacy()
            && is_object( $this->css ) 
                && ( $configtype = $this->get( 'configtype' ) ) 
                    && !empty( $configtype ) && 'theme' != $configtype ):
            return FALSE;
        endif;
        return TRUE;
    }   
    
    function get_current_parent() {        
        if ( $parent = $this->get( 'parnt' ) )
            return $parent;
        else return get_template();
    }  
   
    function get_current_child() {       
        if ( $child = $this->get( 'child' ) )
            return $child;
        else return get_stylesheet();
    }        
    function toggle_debug() {
        $debug = '';
        if ( $_POST[ 'ctc_is_debug' ] ):
            $this->is_debug = 1;
        else:
            $this->is_debug = 0;
        endif;
        update_option( MS_CHILD_THEME_EDITOR . '_debug', $this->is_debug, FALSE );
        delete_site_transient( MS_CHILD_THEME_EDITOR . '_debug' );
    }
    
    function debug( $msg = NULL, $fn = NULL, $cl = NULL ) {
        if ( $this->is_debug )
            $this->debug .= ( isset( $cl ) ? $cl . '::' : '' ) . ( isset( $fn ) ? $fn . ' -- ' : '' ) . ( isset( $msg ) ? $msg . LF : '' );
    }
    
    function log_debug() {
        $this->debug( '*** END OF REQUEST ***', __FUNCTION__, __CLASS__ );
       
        set_site_transient( MS_CHILD_THEME_EDITOR . '_debug', $this->debug, 3600 );
    }
    function get_debug() {
        return get_site_transient( MS_CHILD_THEME_EDITOR . '_debug' ) . LF . $this->debug;
    }
    function cache_debug() {
        $this->debug( '*** END OF REQUEST ***', __FUNCTION__, __CLASS__ );
        $this->updates[] = array(
            'obj'   => 'debug',
            'key'   => '',
            'data'  => $this->debug,
        );
    }
    function parse_parent_stylesheet_to_source() {
        $this->css->parse_css_file( 'parnt' );
    }
    
    function parse_child_stylesheet_to_source() {
        $this->css->parse_css_file( 'child', 'style.css', 'parnt' );
    }
    
    function parse_child_stylesheet_to_target() {
        $this->css->parse_css_file( 'child', 'style.css' );
    }
    
    function parse_custom_stylesheet_to_target() {
        $this->css->parse_css_file( 'child', 'ms-separate-style.css' );
    }
        
    function parse_genesis_stylesheet_to_source() {
        $this->css->parse_css_file( 'child', 'ctc-genesis.css', 'parnt' );
    }
        
    function parse_additional_stylesheets_to_source() {       
		foreach ( $this->css->addl_css as $file ):
			$this->css->parse_css_file( 'parnt', $file );
		endforeach;
		$this->debug( print_r( $this->css->addl_css, TRUE ), __FUNCTION__, __CLASS__ );
    }
    
    function reset_child_theme() {
        $parnt  = $this->get( 'parnt' );
        $child  = $this->get( 'child' );
        $name   = $this->get( 'child_name' );
        $this->css = new ms_theme_editor_css();
        $this->css->set_prop( 'parnt', $parnt );
        $this->css->set_prop( 'child', $child );
        $this->css->set_prop( 'child_name', $name );
        $this->css->set_prop( 'enqueue', 'enqueue' );
        $this->backup_or_restore_file( 'header.php', TRUE );
        $this->delete_child_file( 'header.msbackup', 'php' );
        $this->backup_or_restore_file( 'style.css', TRUE );
        $this->delete_child_file( 'style.msbackup', 'css' );
        $this->backup_or_restore_file( 'ms-style.css', TRUE );
        $this->delete_child_file( 'ms-style.msbackup', 'css' );
    }
    
    function copy_theme_mods( $from, $to ) {
        if ( strlen( $from ) && strlen( $to ) ):
            $this->set_theme_mods( $to, $this->get_theme_mods( $from ) );
            do_action( 'chld_thm_cfg_copy_theme_mods', $from, $to );
        endif;
    }   
    
    function get_theme_mods( $theme ){
        
        $active_theme = get_stylesheet();
        
        $mods = get_option( 'theme_mods_' . $theme );
        if ( $active_theme == $theme ):
            $this->debug( 'from is active, using active widgets', __FUNCTION__, __CLASS__ );
            
            $mods[ 'sidebars_widgets' ][ 'data' ] = retrieve_widgets();
        else:
            $this->debug( 'from not active, using theme mods widgets', __FUNCTION__, __CLASS__ );
            
            $mods[ 'sidebars_widgets' ][ 'data' ] = empty( $mods[ 'sidebars_widgets' ][ 'data' ] ) ?
                array( 'wp_inactive_widgets' => array() ) : $mods[ 'sidebars_widgets' ][ 'data' ];
        endif;
        return $mods;
    }
    
    function set_theme_mods( $theme, $mods ){
        $active_theme = get_stylesheet();
        $widgets = $mods[ 'sidebars_widgets' ][ 'data' ];
        if ( $active_theme == $theme ):
            $this->debug( 'to active, setting active widgets', __FUNCTION__, __CLASS__ );
            
            wp_set_sidebars_widgets( $mods[ 'sidebars_widgets' ][ 'data' ] );
            
            unset( $mods[ 'sidebars_widgets' ] );
        else:
            $this->debug( 'child not active, saving widgets in theme mods', __FUNCTION__, __CLASS__ );
            
            $mods[ 'sidebars_widgets' ][ 'time' ] = time();
        endif;
        
        update_option( 'theme_mods_' . $theme, $mods );
    }
    
    function network_enable() {
        if ( $child = $this->get( 'child' ) ):
            $allowed_themes = get_site_option( 'allowedthemes' );
            $allowed_themes[ $child ] = true;
            update_site_option( 'allowedthemes', $allowed_themes );
        endif;
    }
    
    function backup_or_restore_file( $source, $restore = FALSE, $target = NULL ){
        $action = $restore ? 'Restore' : 'Backup';
        $this->debug( LF . LF . $action . ' main stylesheet...', __FUNCTION__, __CLASS__ );
        if ( !$this->fs ): 
            $this->debug( 'No filesystem access, returning', __FUNCTION__, __CLASS__ );
            return FALSE; 
        endif;
        list( $base, $suffix ) = explode( '.', $source );
        if ( empty( $target ) )
            $target = $base . '.msbackup.' . $suffix;
        if ( $restore ):
            $source = $target;
            $target = $base . '.' . $suffix;        
        endif;
        $fstarget = $this->fspath( $this->css->get_child_target( $target ) );
        $fssource = $this->fspath( $this->css->get_child_target( $source ) );
        global $wp_filesystem;
        if ( ( !$wp_filesystem->exists( $fssource ) ) || ( !$restore && $wp_filesystem->exists( $fstarget ) ) ):
            $this->debug( 'No stylesheet, returning', __FUNCTION__, __CLASS__ );
            return FALSE;
        endif;
        if ( $wp_filesystem->copy( $fssource, $fstarget, FS_CHMOD_FILE ) ):
            $this->debug( 'Filesystem ' . $action . ' successful', __FUNCTION__, __CLASS__ );
            return TRUE;
        else:
            $this->debug( 'Filesystem ' . $action . ' failed', __FUNCTION__, __CLASS__ );
            return FALSE;
        endif;
    }
    
    function rewrite_stylesheet_header(){
        $this->debug( LF . LF . 'Rewriting main stylesheet header...', __FUNCTION__, __CLASS__ );
        if ( !$this->fs ): 
            $this->debug( 'No filesystem access, returning', __FUNCTION__, __CLASS__ );
            return FALSE; 
        endif;
        $origcss        = $this->css->get_child_target( 'style.css' );
        $fspath         = $this->fspath( $origcss );
        global $wp_filesystem;
        if( !$wp_filesystem->exists( $fspath ) ): 
            $this->debug( 'No stylesheet, returning', __FUNCTION__, __CLASS__ );
            return FALSE;
        endif;
       
        $contents       = $wp_filesystem->get_contents( $fspath );
        $child_headers  = $this->css->get_css_header();
        if ( is_array( $child_headers ) )
            $regex      = implode( '|', array_map( 'preg_quote', array_keys( $child_headers ) ) );
        else $regex     = 'NO HEADERS';
        $regex          = '/(' . $regex . '):.*$/';
        $this->debug( 'regex: ' . $regex, __FUNCTION__, __CLASS__ );
        $header         = str_replace( "\r", LF, substr( $contents, 0, 8192 ) );
        $contents       = substr( $contents, 8192 );
        $this->debug( 'original header: ' . LF . substr( $header, 0, 1024 ), __FUNCTION__, __CLASS__ );
       
        $header = preg_replace( '#\@import\s+url\(.+?\);\s*#s', '', $header );
       
        $headerdata     = explode( "\n", $header );
        $in_comment     = 0;
        $found_header   = 0;
        $headerdone     = 0;
        $newheader      = '';
        if ( $headerdata ):
            $this->debug( 'parsing header...', __FUNCTION__, __CLASS__ );
            foreach ( $headerdata as $n => $headerline ):
                preg_match_all("/(\*\/|\/\*)/", $headerline, $matches );
                if ( $matches ):
                    foreach ( $matches[1] as $token ): 
                        if ( '/*' == $token ):
                            $in_comment = 1;
                        elseif ( '*/' == $token ):
                            $in_comment = 0;
                        endif;
                    endforeach;
                endif;
                if ( $in_comment ):
                    $this->debug( 'in comment', __FUNCTION__, __CLASS__ );
                    if ( preg_match( $regex, $headerline, $matches ) && !empty( $matches[ 1 ] ) ):
                        $found_header = 1;
                        $key = $matches[ 1 ];
                        $this->debug( 'found header: ' . $key, __FUNCTION__, __CLASS__ );
                        if ( array_key_exists( $key, $child_headers ) ):
                            $this->debug( 'child header value exists: ', __FUNCTION__, __CLASS__ );
                            $value = trim( $child_headers[ $key ] );
                            unset( $child_headers[ $key ] );
                            if ( $value ):
                                $this->debug( 'setting ' . $key . ' to ' . $value, __FUNCTION__, __CLASS__ );
                                $count = 0;
                                $headerline = preg_replace( 
                                    $regex, 
                                    ( empty( $value ) ? '' : $key . ': ' . $value ), 
                                    $headerline
                                );
                            else:
                                $this->debug( 'removing ' . $key, __FUNCTION__, __CLASS__ );
                                continue;
                            endif;
                        endif;
                    endif;
                    $newheader .= $headerline . LF;
                elseif ( $found_header && !$headerdone ): 
                    foreach ( $child_headers as $key => $value ):
                        $this->debug( 'inserting ' . $key . ': ' . $value, __FUNCTION__, __CLASS__ );
                        if ( empty( $value ) ) continue;
                        $newheader .= $key . ': ' . trim( $value ) . "\n";
                    endforeach;
                   
                    $newheader .= $headerline . "\n" . $this->css->get_css_imports();
                    $headerdone = 1;
                else:
                  
                    $newheader .= $headerline . LF;
                endif;
            endforeach;
            $this->debug( 'new header: ' . LF . substr( $newheader, 0, 1024 ), __FUNCTION__, __CLASS__ );
            if ( !$found_header ) return FALSE;
        endif;
        $contents = $newheader . $contents;
        if ( FALSE === $wp_filesystem->put_contents( $fspath, $contents ) ):
        else:           
        endif;
    }    
  
    function get_child_stylesheet() {
        $handling = $this->get( 'handling' );
        if ( 'separate' == $handling )
            return 'ms-separate-style.css';
        elseif ( 'reset' == $this->childtype )
            return FALSE;
        else
            return 'style.css';
    }
   
    function repair_header() {
        
        if ( ! $this->get( 'cssunreg' ) && !$this->get( 'csswphead' ) ) return;
        $this->debug( 'repairing parent header', __FUNCTION__, __CLASS__ );
        
        $this->copy_parent_file( 'header' );
        
        $this->backup_or_restore_file( 'header.php' );
        
        global $wp_filesystem;
        $cssstr = "get_template_directory_uri()";
        $wphstr = '<?php // MODIFIED BY CTC' . LF . 'wp_head();' . LF . '?>' . LF . '</head>';
        $filename = $this->css->get_child_target( 'header.php' );
        $contents = $wp_filesystem->get_contents( $this->fspath( $filename ) );
        
        
        if ( $this->get( 'cssunreg' ) || $this->get( 'csswphead' ) ):
            $repairs = 0;
            $contents = preg_replace( "#(get_bloginfo\(\s*['\"]stylesheet_url['\"]\s*\)|get_stylesheet_uri\(\s*\))#s", $cssstr . ' . "/style.css"', $contents, -1, $count ); 
            $repairs += $count;
            $contents = preg_replace( "#([^_])bloginfo\(\s*['\"]stylesheet_url['\"]\s*\)#s", "$1echo " . $cssstr . ' . "/style.css"', $contents, -1, $count );
            $repairs += $count;
            $contents = preg_replace( "#([^_])bloginfo\(\s*['\"]stylesheet_directory['\"]\s*\)#s", "$1echo " . $cssstr, $contents, -1, $count );
            $repairs += $count;
            $contents = preg_replace( "#(trailingslashit\()?(\s*)get_stylesheet_directory_uri\(\s*\)(\s*\))?\s*\.\s*['\"]\/?([\w\-\.\/]+?)\.css['\"]#s", 
                "$2echo $cssstr . '$3.css'", $contents, -1, $count );
            $repairs += $count;
            if ( $repairs )
                $this->css->set_prop( 'parntloaded', TRUE );
        endif;

        
        if ( $this->get( 'csswphead' ) ):
            $contents = preg_replace( "#wp_head\(\s*\)\s*;#s", '', $contents );
            $contents = preg_replace( "#</head>#s", $wphstr, $contents );
            $contents = preg_replace( "#\s*<\?php\s*\?>\s*#s", LF, $contents ); // clean up
        endif;
        
        $this->debug( 'Writing to filesystem: ' . $filename . LF . $contents, __FUNCTION__, __CLASS__ );
        if ( FALSE === $wp_filesystem->put_contents( $this->fspath( $filename ), $contents ) ):
            $this->debug( 'Filesystem write failed, returning.', __FUNCTION__, __CLASS__ );
            return FALSE; 
        endif;
    }
    
    function evaluate_signals() {
        if ( !isset( $_POST[ 'ms_theme_child_analysis' ] ) ) return;
        $analysis   = json_decode( urldecode( $_POST[ 'ms_theme_child_analysis' ] ) );
        
        $unregs     = array( 'thm_past_wphead', 'thm_unregistered', 'dep_unregistered', 'css_past_wphead', 'dep_past_wphead' );
        
        $baseline = $this->get( 'ignoreparnt' ) ? 'child' : 'parnt';
        $this->debug( 'baseline: ' . $baseline, __FUNCTION__, __CLASS__ );

       
        $this->css->parnt_deps  = array();
        $this->css->child_deps  = array();
        $this->css->addl_css    = array();
        
        
        if ( isset( $analysis->parnt->imports ) ):
            foreach ( $analysis->parnt->imports as $import ):
                if ( preg_match( '%(https?:)?//%', $import ) ) continue; 
                $this->css->addl_css[] = sanitize_text_field( $import );
            endforeach;
        endif;

        
        if ( isset( $analysis->{ $baseline } ) ):
            if ( isset( $analysis->{ $baseline }->deps ) ):
                foreach ( $analysis->{ $baseline }->deps[ 0 ] as $deparray ):
                   
                    if ( 'chld_thm_cfg_parent' == $deparray[ 0 ] )
                        continue;
                    if ( !in_array( $deparray[ 0 ], $unregs ) ):
                          $this->css->parnt_deps[] = $deparray[ 0 ];
                    endif;
                    if ( !preg_match( "/^style.*?\.css$/", $deparray[ 1 ] ) ):
                        $this->css->addl_css[] = sanitize_text_field( $deparray[ 1 ] );
                    endif;
                endforeach;
                foreach ( $analysis->{ $baseline }->deps[ 1 ] as $deparray ):
                    if ( 'chld_thm_cfg_child' == $deparray[ 0 ] )
                        continue;
                    if ( !in_array( $deparray[ 0 ], $unregs ) ):
                        $this->css->child_deps[] = $deparray[ 0 ];
                    endif;
                    if ( 'separate' == $this->get( 'handling' ) || !empty( $analysis->{ $baseline }->signals->ctc_child_loaded ) ):
                        if ( !preg_match( "/^style.*?\.css$/", $deparray[ 1 ] ) ):
                            $this->css->addl_css[] = sanitize_text_field( $deparray[ 1 ] );
                        endif;
                    endif;
                endforeach;
            endif;
        endif;
        
        if ( isset( $analysis->{ $baseline }->signals ) ):
            $this->css->set_prop( 'hasstyles', isset( $analysis->{ $baseline }->signals->thm_no_styles ) ? 0 : 1 );
            $this->css->set_prop( 'csswphead', isset( $analysis->{ $baseline }->signals->thm_past_wphead ) ? 1 : 0 );
            $this->css->set_prop( 'cssunreg', isset( $analysis->{ $baseline }->signals->thm_unregistered ) ? 1 : 0 );
            if ( isset( $analysis->{ $baseline }->signals->thm_parnt_loaded ) ):
                $this->set_enqueue_priority( $analysis, $baseline );
            endif;
        endif;
        
        if ( isset( $analysis->child->signals->thm_past_wphead ) )
            $this->css->set_prop( 'csswphead', 1 );
        if ( isset( $analysis->child->signals->thm_unregistered ) )
            $this->css->set_prop( 'cssunreg', 1 );
      
        if ( isset( $analysis->child->signals->thm_notheme ) )
            $this->css->set_prop( 'cssnotheme', 1 );
        if ( isset( $analysis->child->signals->thm_child_loaded ) ):
            $this->css->set_prop( 'childloaded', $analysis->child->signals->thm_child_loaded );
            $this->set_enqueue_priority( $analysis, 'child' );
        else:
            $this->css->set_prop( 'childloaded',  0 );
        endif;
        
        if ( isset( $analysis->child->signals->thm_parnt_loaded ) ):
            $this->css->set_prop( 'parntloaded',  $analysis->child->signals->thm_parnt_loaded );
            if ( 'thm_unregistered' != $analysis->child->signals->thm_parnt_loaded ):
                array_unshift( $this->css->child_deps, $analysis->child->signals->thm_parnt_loaded );
            endif;
        else:
            $this->css->set_prop( 'parntloaded',  0 );
        endif;            
        
        if ( isset( $analysis->child->signals->ctc_parnt_reorder ) )
            $this->css->set_prop( 'reorder', 1 );
       
        if ( isset( $analysis->child->signals->ctc_gen_loaded ) )
            $this->genesis = TRUE;
    }    
    
    function set_enqueue_priority( $analysis, $baseline ){
        foreach ( $analysis->{ $baseline }->irreg as $irreg ):
            $handles = explode( ',', $irreg );
            $priority = array_shift( $handles );
            $handle = $analysis->{ $baseline }->signals->{ 'thm_' . $baseline . '_loaded' };
            if ( in_array( $handle, $handles ) ):
                $this->debug( '(baseline: ' . $baseline . ') match: ' . $handle . ' setting priority: ' . $priority, __FUNCTION__, __CLASS__ );
                $this->css->set_prop( 'qpriority', $priority );
                break;
            endif;
        endforeach;
    }
}